Merge "VectorDrawable native rendering - Step 3 of MANY"
diff --git a/core/java/android/util/PathParser.java b/core/java/android/util/PathParser.java
index f17a16c..78d5bcd 100644
--- a/core/java/android/util/PathParser.java
+++ b/core/java/android/util/PathParser.java
@@ -82,6 +82,10 @@
}
}
+ 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 bc3ac5f..30593f2 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -49,6 +49,7 @@
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 c44a62c..63f193d 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -130,6 +130,7 @@
extern int register_android_graphics_Region(JNIEnv* env);
extern int register_android_graphics_SurfaceTexture(JNIEnv* env);
extern int register_android_graphics_Xfermode(JNIEnv* env);
+extern int register_android_graphics_drawable_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 @@
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 0000000..53d4c6a
--- /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 3761a99..6526021 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.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.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.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 @@
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 @@
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 @@
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 @@
*/
private VectorDrawable(@NonNull VectorDrawableState state, @Nullable Resources res) {
mVectorState = state;
-
updateLocalState(res);
}
@@ -262,7 +235,7 @@
* 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 @@
}
Object getTargetByName(String name) {
- return mVectorState.mVPathRenderer.mVGTargetsMap.get(name);
+ return mVectorState.mVGTargetsMap.get(name);
}
@Override
@@ -310,61 +283,23 @@
// 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 @@
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 @@
* 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 @@
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 @@
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 @@
state.mCacheDirty = true;
updateStateFromTypedArray(a);
} catch (XmlPullParserException e) {
- rethrowAsRuntimeException(e);
+ throw new RuntimeException(e);
} finally {
a.recycle();
}
@@ -525,8 +458,8 @@
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 @@
* @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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
void setAllowCaching(boolean allowCaching) {
- mAllowCaching = allowCaching;
+ nSetAllowCaching(mVectorState.getNativeRenderer(), allowCaching);
}
private boolean needMirroring() {
@@ -778,84 +705,68 @@
}
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;
+ @Override
+ public void finalize() throws Throwable {
+ if (mNativeRendererPtr != 0) {
+ nDestroyRenderer(mNativeRendererPtr);
+ mNativeRendererPtr = 0;
}
-
- if (mTempPaint == null) {
- mTempPaint = new Paint();
- mTempPaint.setFilterBitmap(true);
- }
- mTempPaint.setAlpha(mVPathRenderer.getRootAlpha());
- mTempPaint.setColorFilter(filter);
- return mTempPaint;
+ super.finalize();
}
- public void updateCachedBitmap(int width, int height) {
- mCachedBitmap.eraseColor(Color.TRANSPARENT);
- Canvas tmpCanvas = new Canvas(mCachedBitmap);
- mVPathRenderer.draw(tmpCanvas, width, height, null);
- }
- 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 @@
&& mCachedThemeAttrs == mThemeAttrs
&& mCachedTint == mTint
&& mCachedTintMode == mTintMode
- && mCachedAutoMirrored == mAutoMirrored
- && mCachedRootAlpha == mVPathRenderer.getRootAlpha()) {
+ && mCachedAutoMirrored == mAutoMirrored) {
return true;
}
+ updateCacheStates();
return false;
}
@@ -876,21 +787,25 @@
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 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();
+ || (mRootGroup != null && mRootGroup.isStateful());
}
- 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;
- }
-
- 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 @@
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();
+ /**
+ * 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 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);
- }
-
- 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 = " ";
+ 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;
- // mStackedMatrix is only used temporarily when drawing, it combines all
- // the parents' local matrices with the current one.
- private final Matrix mStackedMatrix = new Matrix();
-
+ // 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 @@
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 @@
} 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 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 @@
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 @@
}
@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 @@
}
}
- 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 @@
*/
private static abstract class VPath implements VObject {
protected PathParser.PathData mPathData = null;
+
String mPathName;
int mChangingConfigurations;
@@ -1379,10 +1166,6 @@
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 @@
@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 @@
* 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 @@
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 @@
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,101 +1323,8 @@
}
@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);
- }
- }
-
- @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);
- }
-
- /**
- * 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);
- }
-
- strokePaint.setStrokeMiter(mStrokeMiterlimit);
- strokePaint.setColor(applyAlpha(mStrokeColor, mStrokeAlpha));
- strokePaint.setColorFilter(filter);
- strokePaint.setStrokeWidth(mStrokeWidth * strokeScale);
- canvas.drawPath(path, strokePaint);
- }
-
- /**
- * 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();
- }
- 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);
- }
-
- // Fix bug in measure.
- path.rLineTo(0, 0);
+ public long getNativePtr() {
+ return mNativePtr;
}
@Override
@@ -1743,7 +1335,44 @@
a.recycle();
}
+ @Override
+ protected void finalize() throws Throwable {
+ if (mNativePtr != 0) {
+ nDestroy(mNativePtr);
+ mNativePtr = 0;
+ }
+ super.finalize();
+ }
+
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];
+ }
+ // 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");
+ }
+
+ 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);
+
// Account for any configuration changes.
mChangingConfigurations |= a.getChangingConfigurations();
@@ -1753,11 +1382,13 @@
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 @@
// 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 @@
// 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();
}
+ fillAlpha = a.getFloat(R.styleable.VectorDrawablePath_fillAlpha, fillAlpha);
- 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);
+ 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 @@
/* 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 0d1ee46..f48c509 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -86,7 +86,7 @@
TextDropShadowCache.cpp \
Texture.cpp \
TextureCache.cpp \
- VectorDrawablePath.cpp \
+ VectorDrawable.cpp \
protos/hwui.proto
hwui_test_common_src_files := \
diff --git a/libs/hwui/PathParser.h b/libs/hwui/PathParser.h
index 4c87b18..c4bbb74 100644
--- a/libs/hwui/PathParser.h
+++ b/libs/hwui/PathParser.h
@@ -17,7 +17,7 @@
#ifndef ANDROID_HWUI_PATHPARSER_H
#define ANDROID_HWUI_PATHPARSER_H
-#include "VectorDrawablePath.h"
+#include "VectorDrawable.h"
#include "utils/VectorDrawableUtils.h"
#include <jni.h>
diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp
new file mode 100644
index 0000000..e13a2bb
--- /dev/null
+++ b/libs/hwui/VectorDrawable.cpp
@@ -0,0 +1,485 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "VectorDrawable.h"
+
+#include "PathParser.h"
+#include "SkImageInfo.h"
+#include <utils/Log.h>
+#include "utils/Macros.h"
+#include "utils/VectorDrawableUtils.h"
+
+#include <math.h>
+#include <string.h>
+
+namespace android {
+namespace uirenderer {
+namespace VectorDrawable {
+
+const int Tree::MAX_CACHED_BITMAP_SIZE = 2048;
+
+void Path::draw(Canvas* outCanvas, const SkMatrix& groupStackedMatrix, float scaleX, float scaleY) {
+ float matrixScale = getMatrixScale(groupStackedMatrix);
+ if (matrixScale == 0) {
+ // When either x or y is scaled to 0, we don't need to draw anything.
+ return;
+ }
+
+ const SkPath updatedPath = getUpdatedPath();
+ SkMatrix pathMatrix(groupStackedMatrix);
+ pathMatrix.postScale(scaleX, scaleY);
+
+ //TODO: try apply the path matrix to the canvas instead of creating a new path.
+ SkPath renderPath;
+ renderPath.reset();
+ renderPath.addPath(updatedPath, pathMatrix);
+
+ float minScale = fmin(scaleX, scaleY);
+ float strokeScale = minScale * matrixScale;
+ drawPath(outCanvas, renderPath, strokeScale);
+}
+
+void Path::setPathData(const Data& data) {
+ if (mData == data) {
+ return;
+ }
+ // Updates the path data. Note that we don't generate a new Skia path right away
+ // because there are cases where the animation is changing the path data, but the view
+ // that hosts the VD has gone off screen, in which case we won't even draw. So we
+ // postpone the Skia path generation to the draw time.
+ mData = data;
+ mSkPathDirty = true;
+}
+
+void Path::dump() {
+ ALOGD("Path: %s has %zu points", mName.c_str(), mData.points.size());
+}
+
+float Path::getMatrixScale(const SkMatrix& groupStackedMatrix) {
+ // Given unit vectors A = (0, 1) and B = (1, 0).
+ // After matrix mapping, we got A' and B'. Let theta = the angel b/t A' and B'.
+ // Therefore, the final scale we want is min(|A'| * sin(theta), |B'| * sin(theta)),
+ // which is (|A'| * |B'| * sin(theta)) / max (|A'|, |B'|);
+ // If max (|A'|, |B'|) = 0, that means either x or y has a scale of 0.
+ //
+ // For non-skew case, which is most of the cases, matrix scale is computing exactly the
+ // scale on x and y axis, and take the minimal of these two.
+ // For skew case, an unit square will mapped to a parallelogram. And this function will
+ // return the minimal height of the 2 bases.
+ SkVector skVectors[2];
+ skVectors[0].set(0, 1);
+ skVectors[1].set(1, 0);
+ groupStackedMatrix.mapVectors(skVectors, 2);
+ float scaleX = hypotf(skVectors[0].fX, skVectors[0].fY);
+ float scaleY = hypotf(skVectors[1].fX, skVectors[1].fY);
+ float crossProduct = skVectors[0].cross(skVectors[1]);
+ float maxScale = fmax(scaleX, scaleY);
+
+ float matrixScale = 0;
+ if (maxScale > 0) {
+ matrixScale = fabs(crossProduct) / maxScale;
+ }
+ return matrixScale;
+}
+Path::Path(const char* pathStr, size_t strLength) {
+ PathParser::ParseResult result;
+ PathParser::getPathDataFromString(&mData, &result, pathStr, strLength);
+ if (!result.failureOccurred) {
+ VectorDrawableUtils::verbsToPath(&mSkPath, mData);
+ }
+}
+
+Path::Path(const Data& data) {
+ mData = data;
+ // Now we need to construct a path
+ VectorDrawableUtils::verbsToPath(&mSkPath, data);
+}
+
+Path::Path(const Path& path) : Node(path) {
+ mData = path.mData;
+ VectorDrawableUtils::verbsToPath(&mSkPath, mData);
+}
+
+bool Path::canMorph(const Data& morphTo) {
+ return VectorDrawableUtils::canMorph(mData, morphTo);
+}
+
+bool Path::canMorph(const Path& path) {
+ return canMorph(path.mData);
+}
+
+const SkPath& Path::getUpdatedPath() {
+ if (mSkPathDirty) {
+ mSkPath.reset();
+ VectorDrawableUtils::verbsToPath(&mSkPath, mData);
+ mSkPathDirty = false;
+ }
+ return mSkPath;
+}
+
+void Path::setPath(const char* pathStr, size_t strLength) {
+ PathParser::ParseResult result;
+ mSkPathDirty = true;
+ PathParser::getPathDataFromString(&mData, &result, pathStr, strLength);
+}
+
+FullPath::FullPath(const FullPath& path) : Path(path) {
+ mStrokeWidth = path.mStrokeWidth;
+ mStrokeColor = path.mStrokeColor;
+ mStrokeAlpha = path.mStrokeAlpha;
+ mFillColor = path.mFillColor;
+ mFillAlpha = path.mFillAlpha;
+ mTrimPathStart = path.mTrimPathStart;
+ mTrimPathEnd = path.mTrimPathEnd;
+ mTrimPathOffset = path.mTrimPathOffset;
+ mStrokeMiterLimit = path.mStrokeMiterLimit;
+ mStrokeLineCap = path.mStrokeLineCap;
+ mStrokeLineJoin = path.mStrokeLineJoin;
+}
+
+const SkPath& FullPath::getUpdatedPath() {
+ if (!mSkPathDirty && !mTrimDirty) {
+ return mTrimmedSkPath;
+ }
+ Path::getUpdatedPath();
+ if (mTrimPathStart != 0.0f || mTrimPathEnd != 1.0f) {
+ applyTrim();
+ return mTrimmedSkPath;
+ } else {
+ return mSkPath;
+ }
+}
+
+void FullPath::updateProperties(float strokeWidth, SkColor strokeColor, float strokeAlpha,
+ SkColor fillColor, float fillAlpha, float trimPathStart, float trimPathEnd,
+ float trimPathOffset, float strokeMiterLimit, int strokeLineCap, int strokeLineJoin) {
+ mStrokeWidth = strokeWidth;
+ mStrokeColor = strokeColor;
+ mStrokeAlpha = strokeAlpha;
+ mFillColor = fillColor;
+ mFillAlpha = fillAlpha;
+ mStrokeMiterLimit = strokeMiterLimit;
+ mStrokeLineCap = SkPaint::Cap(strokeLineCap);
+ mStrokeLineJoin = SkPaint::Join(strokeLineJoin);
+
+ // If any trim property changes, mark trim dirty and update the trim path
+ setTrimPathStart(trimPathStart);
+ setTrimPathEnd(trimPathEnd);
+ setTrimPathOffset(trimPathOffset);
+}
+
+inline SkColor applyAlpha(SkColor color, float alpha) {
+ int alphaBytes = SkColorGetA(color);
+ return SkColorSetA(color, alphaBytes * alpha);
+}
+
+void FullPath::drawPath(Canvas* outCanvas, const SkPath& renderPath, float strokeScale){
+ // Draw path's fill, if fill color isn't transparent.
+ if (mFillColor != SK_ColorTRANSPARENT) {
+ mPaint.setStyle(SkPaint::Style::kFill_Style);
+ mPaint.setAntiAlias(true);
+ mPaint.setColor(applyAlpha(mFillColor, mFillAlpha));
+ outCanvas->drawPath(renderPath, mPaint);
+ }
+ // Draw path's stroke, if stroke color isn't transparent
+ if (mStrokeColor != SK_ColorTRANSPARENT) {
+ mPaint.setStyle(SkPaint::Style::kStroke_Style);
+ mPaint.setAntiAlias(true);
+ mPaint.setStrokeJoin(mStrokeLineJoin);
+ mPaint.setStrokeCap(mStrokeLineCap);
+ mPaint.setStrokeMiter(mStrokeMiterLimit);
+ mPaint.setColor(applyAlpha(mStrokeColor, mStrokeAlpha));
+ mPaint.setStrokeWidth(mStrokeWidth * strokeScale);
+ outCanvas->drawPath(renderPath, mPaint);
+ }
+}
+
+/**
+ * Applies trimming to the specified path.
+ */
+void FullPath::applyTrim() {
+ if (mTrimPathStart == 0.0f && mTrimPathEnd == 1.0f) {
+ // No trimming necessary.
+ return;
+ }
+ SkPathMeasure measure(mSkPath, false);
+ float len = SkScalarToFloat(measure.getLength());
+ float start = len * fmod((mTrimPathStart + mTrimPathOffset), 1.0f);
+ float end = len * fmod((mTrimPathEnd + mTrimPathOffset), 1.0f);
+
+ mTrimmedSkPath.reset();
+ if (start > end) {
+ measure.getSegment(start, len, &mTrimmedSkPath, true);
+ measure.getSegment(0, end, &mTrimmedSkPath, true);
+ } else {
+ measure.getSegment(start, end, &mTrimmedSkPath, true);
+ }
+ mTrimDirty = false;
+}
+
+inline int putData(int8_t* outBytes, int startIndex, float value) {
+ int size = sizeof(float);
+ memcpy(&outBytes[startIndex], &value, size);
+ return size;
+}
+
+inline int putData(int8_t* outBytes, int startIndex, int value) {
+ int size = sizeof(int);
+ memcpy(&outBytes[startIndex], &value, size);
+ return size;
+}
+
+struct FullPathProperties {
+ // TODO: Consider storing full path properties in this struct instead of the fields.
+ float strokeWidth;
+ SkColor strokeColor;
+ float strokeAlpha;
+ SkColor fillColor;
+ float fillAlpha;
+ float trimPathStart;
+ float trimPathEnd;
+ float trimPathOffset;
+ int32_t strokeLineCap;
+ int32_t strokeLineJoin;
+ float strokeMiterLimit;
+};
+
+REQUIRE_COMPATIBLE_LAYOUT(FullPathProperties);
+
+static_assert(sizeof(float) == sizeof(int32_t), "float is not the same size as int32_t");
+static_assert(sizeof(SkColor) == sizeof(int32_t), "SkColor is not the same size as int32_t");
+
+bool FullPath::getProperties(int8_t* outProperties, int length) {
+ int propertyDataSize = sizeof(FullPathProperties);
+ if (length != propertyDataSize) {
+ LOG_ALWAYS_FATAL("Properties needs exactly %d bytes, a byte array of size %d is provided",
+ propertyDataSize, length);
+ return false;
+ }
+ // TODO: consider replacing the property fields with a FullPathProperties struct.
+ FullPathProperties properties;
+ properties.strokeWidth = mStrokeWidth;
+ properties.strokeColor = mStrokeColor;
+ properties.strokeAlpha = mStrokeAlpha;
+ properties.fillColor = mFillColor;
+ properties.fillAlpha = mFillAlpha;
+ properties.trimPathStart = mTrimPathStart;
+ properties.trimPathEnd = mTrimPathEnd;
+ properties.trimPathOffset = mTrimPathOffset;
+ properties.strokeLineCap = mStrokeLineCap;
+ properties.strokeLineJoin = mStrokeLineJoin;
+ properties.strokeMiterLimit = mStrokeMiterLimit;
+
+ memcpy(outProperties, &properties, length);
+ return true;
+}
+
+void ClipPath::drawPath(Canvas* outCanvas, const SkPath& renderPath,
+ float strokeScale){
+ outCanvas->clipPath(&renderPath, SkRegion::kIntersect_Op);
+}
+
+Group::Group(const Group& group) : Node(group) {
+ mRotate = group.mRotate;
+ mPivotX = group.mPivotX;
+ mPivotY = group.mPivotY;
+ mScaleX = group.mScaleX;
+ mScaleY = group.mScaleY;
+ mTranslateX = group.mTranslateX;
+ mTranslateY = group.mTranslateY;
+}
+
+void Group::draw(Canvas* outCanvas, const SkMatrix& currentMatrix, float scaleX,
+ float scaleY) {
+ // TODO: Try apply the matrix to the canvas instead of passing it down the tree
+
+ // Calculate current group's matrix by preConcat the parent's and
+ // and the current one on the top of the stack.
+ // Basically the Mfinal = Mviewport * M0 * M1 * M2;
+ // Mi the local matrix at level i of the group tree.
+ SkMatrix stackedMatrix;
+ getLocalMatrix(&stackedMatrix);
+ stackedMatrix.postConcat(currentMatrix);
+
+ // Save the current clip information, which is local to this group.
+ outCanvas->save(SkCanvas::kMatrixClip_SaveFlag);
+ // Draw the group tree in the same order as the XML file.
+ for (Node* child : mChildren) {
+ child->draw(outCanvas, stackedMatrix, scaleX, scaleY);
+ }
+ // Restore the previous clip information.
+ outCanvas->restore();
+}
+
+void Group::dump() {
+ ALOGD("Group %s has %zu children: ", mName.c_str(), mChildren.size());
+ for (size_t i = 0; i < mChildren.size(); i++) {
+ mChildren[i]->dump();
+ }
+}
+
+void Group::updateLocalMatrix(float rotate, float pivotX, float pivotY,
+ float scaleX, float scaleY, float translateX, float translateY) {
+ setRotation(rotate);
+ setPivotX(pivotX);
+ setPivotY(pivotY);
+ setScaleX(scaleX);
+ setScaleY(scaleY);
+ setTranslateX(translateX);
+ setTranslateY(translateY);
+}
+
+void Group::getLocalMatrix(SkMatrix* outMatrix) {
+ outMatrix->reset();
+ // TODO: use rotate(mRotate, mPivotX, mPivotY) and scale with pivot point, instead of
+ // translating to pivot for rotating and scaling, then translating back.
+ outMatrix->postTranslate(-mPivotX, -mPivotY);
+ outMatrix->postScale(mScaleX, mScaleY);
+ outMatrix->postRotate(mRotate, 0, 0);
+ outMatrix->postTranslate(mTranslateX + mPivotX, mTranslateY + mPivotY);
+}
+
+void Group::addChild(Node* child) {
+ mChildren.push_back(child);
+}
+
+bool Group::getProperties(float* outProperties, int length) {
+ int propertyCount = static_cast<int>(Property::Count);
+ if (length != propertyCount) {
+ LOG_ALWAYS_FATAL("Properties needs exactly %d bytes, a byte array of size %d is provided",
+ propertyCount, length);
+ return false;
+ }
+ for (int i = 0; i < propertyCount; i++) {
+ Property currentProperty = static_cast<Property>(i);
+ switch (currentProperty) {
+ case Property::Rotate_Property:
+ outProperties[i] = mRotate;
+ break;
+ case Property::PivotX_Property:
+ outProperties[i] = mPivotX;
+ break;
+ case Property::PivotY_Property:
+ outProperties[i] = mPivotY;
+ break;
+ case Property::ScaleX_Property:
+ outProperties[i] = mScaleX;
+ break;
+ case Property::ScaleY_Property:
+ outProperties[i] = mScaleY;
+ break;
+ case Property::TranslateX_Property:
+ outProperties[i] = mTranslateX;
+ break;
+ case Property::TranslateY_Property:
+ outProperties[i] = mTranslateY;
+ break;
+ default:
+ LOG_ALWAYS_FATAL("Invalid input index: %d", i);
+ return false;
+ }
+ }
+ return true;
+}
+
+void Tree::draw(Canvas* outCanvas, SkColorFilter* colorFilter,
+ const SkRect& bounds, bool needsMirroring, bool canReuseCache) {
+ // The imageView can scale the canvas in different ways, in order to
+ // avoid blurry scaling, we have to draw into a bitmap with exact pixel
+ // size first. This bitmap size is determined by the bounds and the
+ // canvas scale.
+ outCanvas->getMatrix(&mCanvasMatrix);
+ mBounds = bounds;
+ int scaledWidth = (int) (mBounds.width() * mCanvasMatrix.getScaleX());
+ int scaledHeight = (int) (mBounds.height() * mCanvasMatrix.getScaleY());
+ scaledWidth = std::min(Tree::MAX_CACHED_BITMAP_SIZE, scaledWidth);
+ scaledHeight = std::min(Tree::MAX_CACHED_BITMAP_SIZE, scaledHeight);
+
+ if (scaledWidth <= 0 || scaledHeight <= 0) {
+ return;
+ }
+
+ int saveCount = outCanvas->save(SkCanvas::SaveFlags::kMatrixClip_SaveFlag);
+ outCanvas->translate(mBounds.fLeft, mBounds.fTop);
+
+ // Handle RTL mirroring.
+ if (needsMirroring) {
+ outCanvas->translate(mBounds.width(), 0);
+ outCanvas->scale(-1.0f, 1.0f);
+ }
+
+ // At this point, canvas has been translated to the right position.
+ // And we use this bound for the destination rect for the drawBitmap, so
+ // we offset to (0, 0);
+ mBounds.offsetTo(0, 0);
+
+ createCachedBitmapIfNeeded(scaledWidth, scaledHeight);
+ if (!mAllowCaching) {
+ updateCachedBitmap(scaledWidth, scaledHeight);
+ } else {
+ if (!canReuseCache || mCacheDirty) {
+ updateCachedBitmap(scaledWidth, scaledHeight);
+ }
+ }
+ drawCachedBitmapWithRootAlpha(outCanvas, colorFilter, mBounds);
+
+ outCanvas->restoreToCount(saveCount);
+}
+
+void Tree::drawCachedBitmapWithRootAlpha(Canvas* outCanvas, SkColorFilter* filter,
+ const SkRect& originalBounds) {
+ SkPaint* paint;
+ if (mRootAlpha == 1.0f && filter == NULL) {
+ paint = NULL;
+ } else {
+ mPaint.setFilterQuality(kLow_SkFilterQuality);
+ mPaint.setAlpha(mRootAlpha * 255);
+ mPaint.setColorFilter(filter);
+ paint = &mPaint;
+ }
+ outCanvas->drawBitmap(mCachedBitmap, 0, 0, mCachedBitmap.width(), mCachedBitmap.height(),
+ originalBounds.fLeft, originalBounds.fTop, originalBounds.fRight,
+ originalBounds.fBottom, paint);
+}
+
+void Tree::updateCachedBitmap(int width, int height) {
+ mCachedBitmap.eraseColor(SK_ColorTRANSPARENT);
+ Canvas* outCanvas = Canvas::create_canvas(mCachedBitmap);
+ float scaleX = width / mViewportWidth;
+ float scaleY = height / mViewportHeight;
+ mRootNode->draw(outCanvas, SkMatrix::I(), scaleX, scaleY);
+ mCacheDirty = false;
+}
+
+void Tree::createCachedBitmapIfNeeded(int width, int height) {
+ if (!canReuseBitmap(width, height)) {
+ SkImageInfo info = SkImageInfo::Make(width, height,
+ kN32_SkColorType, kPremul_SkAlphaType);
+ mCachedBitmap.setInfo(info);
+ // TODO: Count the bitmap cache against app's java heap
+ mCachedBitmap.allocPixels(info);
+ mCacheDirty = true;
+ }
+}
+
+bool Tree::canReuseBitmap(int width, int height) {
+ return width == mCachedBitmap.width() && height == mCachedBitmap.height();
+}
+
+}; // namespace VectorDrawable
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/VectorDrawable.h b/libs/hwui/VectorDrawable.h
new file mode 100644
index 0000000..6c84b05
--- /dev/null
+++ b/libs/hwui/VectorDrawable.h
@@ -0,0 +1,330 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWUI_VPATH_H
+#define ANDROID_HWUI_VPATH_H
+
+#include "Canvas.h"
+#include <SkBitmap.h>
+#include <SkColor.h>
+#include <SkMatrix.h>
+#include <SkPaint.h>
+#include <SkPath.h>
+#include <SkPathMeasure.h>
+#include <SkRect.h>
+
+#include <cutils/compiler.h>
+#include <stddef.h>
+#include <vector>
+#include <string>
+
+namespace android {
+namespace uirenderer {
+
+namespace VectorDrawable {
+#define VD_SET_PROP_WITH_FLAG(field, value, flag) (VD_SET_PROP(field, value) ? (flag = true, true): false);
+#define VD_SET_PROP(field, value) (value != field ? (field = value, true) : false)
+
+/* A VectorDrawable is composed of a tree of nodes.
+ * Each node can be a group node, or a path.
+ * A group node can have groups or paths as children, but a path node has
+ * no children.
+ * One example can be:
+ * Root Group
+ * / | \
+ * Group Path Group
+ * / \ |
+ * Path Path Path
+ *
+ */
+class ANDROID_API Node {
+public:
+ Node(const Node& node) {
+ mName = node.mName;
+ }
+ Node() {}
+ virtual void draw(Canvas* outCanvas, const SkMatrix& currentMatrix,
+ float scaleX, float scaleY) = 0;
+ virtual void dump() = 0;
+ void setName(const char* name) {
+ mName = name;
+ }
+ virtual ~Node(){}
+protected:
+ std::string mName;
+};
+
+class ANDROID_API Path : public Node {
+public:
+ struct ANDROID_API Data {
+ std::vector<char> verbs;
+ std::vector<size_t> verbSizes;
+ std::vector<float> points;
+ bool operator==(const Data& data) const {
+ return verbs == data.verbs && verbSizes == data.verbSizes
+ && points == data.points;
+ }
+ };
+ Path(const Data& nodes);
+ Path(const Path& path);
+ Path(const char* path, size_t strLength);
+ Path() {}
+ void dump() override;
+ bool canMorph(const Data& path);
+ bool canMorph(const Path& path);
+ void draw(Canvas* outCanvas, const SkMatrix& groupStackedMatrix,
+ float scaleX, float scaleY) override;
+ void setPath(const char* path, size_t strLength);
+ void setPathData(const Data& data);
+ static float getMatrixScale(const SkMatrix& groupStackedMatrix);
+
+protected:
+ virtual const SkPath& getUpdatedPath();
+ virtual void drawPath(Canvas *outCanvas, const SkPath& renderPath,
+ float strokeScale) = 0;
+ Data mData;
+ SkPath mSkPath;
+ bool mSkPathDirty = true;
+};
+
+class ANDROID_API FullPath: public Path {
+public:
+ FullPath(const FullPath& path); // for cloning
+ FullPath(const char* path, size_t strLength) : Path(path, strLength) {}
+ FullPath() : Path() {}
+ FullPath(const Data& nodes) : Path(nodes) {}
+
+ void updateProperties(float strokeWidth, SkColor strokeColor,
+ float strokeAlpha, SkColor fillColor, float fillAlpha,
+ float trimPathStart, float trimPathEnd, float trimPathOffset,
+ float strokeMiterLimit, int strokeLineCap, int strokeLineJoin);
+ float getStrokeWidth() {
+ return mStrokeWidth;
+ }
+ void setStrokeWidth(float strokeWidth) {
+ mStrokeWidth = strokeWidth;
+ }
+ SkColor getStrokeColor() {
+ return mStrokeColor;
+ }
+ void setStrokeColor(SkColor strokeColor) {
+ mStrokeColor = strokeColor;
+ }
+ float getStrokeAlpha() {
+ return mStrokeAlpha;
+ }
+ void setStrokeAlpha(float strokeAlpha) {
+ mStrokeAlpha = strokeAlpha;
+ }
+ SkColor getFillColor() {
+ return mFillColor;
+ }
+ void setFillColor(SkColor fillColor) {
+ mFillColor = fillColor;
+ }
+ float getFillAlpha() {
+ return mFillAlpha;
+ }
+ void setFillAlpha(float fillAlpha) {
+ mFillAlpha = fillAlpha;
+ }
+ float getTrimPathStart() {
+ return mTrimPathStart;
+ }
+ void setTrimPathStart(float trimPathStart) {
+ VD_SET_PROP_WITH_FLAG(mTrimPathStart, trimPathStart, mTrimDirty);
+ }
+ float getTrimPathEnd() {
+ return mTrimPathEnd;
+ }
+ void setTrimPathEnd(float trimPathEnd) {
+ VD_SET_PROP_WITH_FLAG(mTrimPathEnd, trimPathEnd, mTrimDirty);
+ }
+ float getTrimPathOffset() {
+ return mTrimPathOffset;
+ }
+ void setTrimPathOffset(float trimPathOffset) {
+ VD_SET_PROP_WITH_FLAG(mTrimPathOffset, trimPathOffset, mTrimDirty);
+ }
+ bool getProperties(int8_t* outProperties, int length);
+
+protected:
+ const SkPath& getUpdatedPath() override;
+ void drawPath(Canvas* outCanvas, const SkPath& renderPath,
+ float strokeScale) override;
+
+private:
+ // Applies trimming to the specified path.
+ void applyTrim();
+ float mStrokeWidth = 0;
+ SkColor mStrokeColor = SK_ColorTRANSPARENT;
+ float mStrokeAlpha = 1;
+ SkColor mFillColor = SK_ColorTRANSPARENT;
+ float mFillAlpha = 1;
+ float mTrimPathStart = 0;
+ float mTrimPathEnd = 1;
+ float mTrimPathOffset = 0;
+ bool mTrimDirty = true;
+ SkPaint::Cap mStrokeLineCap = SkPaint::Cap::kButt_Cap;
+ SkPaint::Join mStrokeLineJoin = SkPaint::Join::kMiter_Join;
+ float mStrokeMiterLimit = 4;
+ SkPath mTrimmedSkPath;
+ SkPaint mPaint;
+};
+
+class ANDROID_API ClipPath: public Path {
+public:
+ ClipPath(const ClipPath& path) : Path(path) {}
+ ClipPath(const char* path, size_t strLength) : Path(path, strLength) {}
+ ClipPath() : Path() {}
+ ClipPath(const Data& nodes) : Path(nodes) {}
+
+protected:
+ void drawPath(Canvas* outCanvas, const SkPath& renderPath,
+ float strokeScale) override;
+};
+
+class ANDROID_API Group: public Node {
+public:
+ Group(const Group& group);
+ Group() {}
+ float getRotation() {
+ return mRotate;
+ }
+ void setRotation(float rotation) {
+ mRotate = rotation;
+ }
+ float getPivotX() {
+ return mPivotX;
+ }
+ void setPivotX(float pivotX) {
+ mPivotX = pivotX;
+ }
+ float getPivotY() {
+ return mPivotY;
+ }
+ void setPivotY(float pivotY) {
+ mPivotY = pivotY;
+ }
+ float getScaleX() {
+ return mScaleX;
+ }
+ void setScaleX(float scaleX) {
+ mScaleX = scaleX;
+ }
+ float getScaleY() {
+ return mScaleY;
+ }
+ void setScaleY(float scaleY) {
+ mScaleY = scaleY;
+ }
+ float getTranslateX() {
+ return mTranslateX;
+ }
+ void setTranslateX(float translateX) {
+ mTranslateX = translateX;
+ }
+ float getTranslateY() {
+ return mTranslateY;
+ }
+ void setTranslateY(float translateY) {
+ mTranslateY = translateY;
+ }
+ virtual void draw(Canvas* outCanvas, const SkMatrix& currentMatrix,
+ float scaleX, float scaleY) override;
+ void updateLocalMatrix(float rotate, float pivotX, float pivotY,
+ float scaleX, float scaleY, float translateX, float translateY);
+ void getLocalMatrix(SkMatrix* outMatrix);
+ void addChild(Node* child);
+ void dump() override;
+ bool getProperties(float* outProperties, int length);
+
+private:
+ enum class Property {
+ Rotate_Property = 0,
+ PivotX_Property,
+ PivotY_Property,
+ ScaleX_Property,
+ ScaleY_Property,
+ TranslateX_Property,
+ TranslateY_Property,
+ // Count of the properties, must be at the end.
+ Count,
+ };
+ float mRotate = 0;
+ float mPivotX = 0;
+ float mPivotY = 0;
+ float mScaleX = 1;
+ float mScaleY = 1;
+ float mTranslateX = 0;
+ float mTranslateY = 0;
+ std::vector<Node*> mChildren;
+};
+
+class ANDROID_API Tree {
+public:
+ Tree(Group* rootNode) : mRootNode(rootNode) {}
+ void draw(Canvas* outCanvas, SkColorFilter* colorFilter,
+ const SkRect& bounds, bool needsMirroring, bool canReuseCache);
+ void drawCachedBitmapWithRootAlpha(Canvas* outCanvas, SkColorFilter* filter,
+ const SkRect& originalBounds);
+
+ void updateCachedBitmap(int width, int height);
+ void createCachedBitmapIfNeeded(int width, int height);
+ bool canReuseBitmap(int width, int height);
+ void setAllowCaching(bool allowCaching) {
+ mAllowCaching = allowCaching;
+ }
+ bool setRootAlpha(float rootAlpha) {
+ return VD_SET_PROP(mRootAlpha, rootAlpha);
+ }
+
+ float getRootAlpha() {
+ return mRootAlpha;
+ }
+ void setViewportSize(float viewportWidth, float viewportHeight) {
+ mViewportWidth = viewportWidth;
+ mViewportHeight = viewportHeight;
+ }
+
+private:
+ // Cap the bitmap size, such that it won't hurt the performance too much
+ // and it won't crash due to a very large scale.
+ // The drawable will look blurry above this size.
+ const static int MAX_CACHED_BITMAP_SIZE;
+
+ bool mCacheDirty = true;
+ bool mAllowCaching = true;
+ float mViewportWidth = 0;
+ float mViewportHeight = 0;
+ float mRootAlpha = 1.0f;
+
+ Group* mRootNode;
+ SkRect mBounds;
+ SkMatrix mCanvasMatrix;
+ SkPaint mPaint;
+ SkPathMeasure mPathMeasure;
+ SkBitmap mCachedBitmap;
+
+};
+
+} // namespace VectorDrawable
+
+typedef VectorDrawable::Path::Data PathData;
+} // namespace uirenderer
+} // namespace android
+
+#endif // ANDROID_HWUI_VPATH_H
diff --git a/libs/hwui/VectorDrawablePath.cpp b/libs/hwui/VectorDrawablePath.cpp
deleted file mode 100644
index c9a54ca..0000000
--- a/libs/hwui/VectorDrawablePath.cpp
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "VectorDrawablePath.h"
-
-#include "PathParser.h"
-#include "utils/VectorDrawableUtils.h"
-
-#include <math.h>
-#include <utils/Log.h>
-
-namespace android {
-namespace uirenderer {
-
-
-VectorDrawablePath::VectorDrawablePath(const char* pathStr, size_t strLength) {
- PathParser::ParseResult result;
- PathParser::getPathDataFromString(&mData, &result, pathStr, strLength);
- if (!result.failureOccurred) {
- VectorDrawableUtils::verbsToPath(&mSkPath, mData);
- }
-}
-
-VectorDrawablePath::VectorDrawablePath(const PathData& data) {
- mData = data;
- // Now we need to construct a path
- VectorDrawableUtils::verbsToPath(&mSkPath, data);
-}
-
-VectorDrawablePath::VectorDrawablePath(const VectorDrawablePath& path) {
- mData = path.mData;
- VectorDrawableUtils::verbsToPath(&mSkPath, mData);
-}
-
-
-bool VectorDrawablePath::canMorph(const PathData& morphTo) {
- return VectorDrawableUtils::canMorph(mData, morphTo);
-}
-
-bool VectorDrawablePath::canMorph(const VectorDrawablePath& path) {
- return canMorph(path.mData);
-}
-
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/VectorDrawablePath.h b/libs/hwui/VectorDrawablePath.h
deleted file mode 100644
index 2e56349..0000000
--- a/libs/hwui/VectorDrawablePath.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HWUI_VPATH_H
-#define ANDROID_HWUI_VPATH_H
-
-#include <cutils/compiler.h>
-#include "SkPath.h"
-#include <vector>
-
-namespace android {
-namespace uirenderer {
-
-struct ANDROID_API PathData {
- // TODO: Try using FatVector instead of std::vector and do a micro benchmark on the performance
- // difference.
- std::vector<char> verbs;
- std::vector<size_t> verbSizes;
- std::vector<float> points;
- bool operator== (const PathData& data) const {
- return verbs == data.verbs && verbSizes == data.verbSizes && points == data.points;
- }
-
-};
-
-class VectorDrawablePath {
-public:
- VectorDrawablePath(const PathData& nodes);
- VectorDrawablePath(const VectorDrawablePath& path);
- VectorDrawablePath(const char* path, size_t strLength);
- bool canMorph(const PathData& path);
- bool canMorph(const VectorDrawablePath& path);
-
-private:
- PathData mData;
- SkPath mSkPath;
-};
-
-} // namespace uirenderer
-} // namespace android
-
-#endif // ANDROID_HWUI_VPATH_H
diff --git a/libs/hwui/tests/microbench/PathParserBench.cpp b/libs/hwui/tests/microbench/PathParserBench.cpp
index 3d9fafa..bd742c6 100644
--- a/libs/hwui/tests/microbench/PathParserBench.cpp
+++ b/libs/hwui/tests/microbench/PathParserBench.cpp
@@ -17,7 +17,7 @@
#include <benchmark/Benchmark.h>
#include "PathParser.h"
-#include "VectorDrawablePath.h"
+#include "VectorDrawable.h"
#include <SkPath.h>
diff --git a/libs/hwui/tests/unit/VectorDrawableTests.cpp b/libs/hwui/tests/unit/VectorDrawableTests.cpp
index 77dd73a..7208547 100644
--- a/libs/hwui/tests/unit/VectorDrawableTests.cpp
+++ b/libs/hwui/tests/unit/VectorDrawableTests.cpp
@@ -17,6 +17,7 @@
#include <gtest/gtest.h>
#include "PathParser.h"
+#include "VectorDrawable.h"
#include "utils/MathUtils.h"
#include "utils/VectorDrawableUtils.h"
@@ -98,6 +99,65 @@
}
},
+ // Check box VectorDrawable path data
+ {
+ // Path
+ "M 0.0,-1.0 l 0.0,0.0 c 0.5522847498,0.0 1.0,0.4477152502 1.0,1.0 l 0.0,0.0 c 0.0,0.5522847498 -0.4477152502,1.0 -1.0,1.0 l 0.0,0.0 c -0.5522847498,0.0 -1.0,-0.4477152502 -1.0,-1.0 l 0.0,0.0 c 0.0,-0.5522847498 0.4477152502,-1.0 1.0,-1.0 Z M 7.0,-9.0 c 0.0,0.0 -14.0,0.0 -14.0,0.0 c -1.1044921875,0.0 -2.0,0.8955078125 -2.0,2.0 c 0.0,0.0 0.0,14.0 0.0,14.0 c 0.0,1.1044921875 0.8955078125,2.0 2.0,2.0 c 0.0,0.0 14.0,0.0 14.0,0.0 c 1.1044921875,0.0 2.0,-0.8955078125 2.0,-2.0 c 0.0,0.0 0.0,-14.0 0.0,-14.0 c 0.0,-1.1044921875 -0.8955078125,-2.0 -2.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z",
+ {
+ {'M', 'l', 'c', 'l', 'c', 'l', 'c', 'l', 'c', 'Z', 'M', 'c', 'c', 'c', 'c', 'c', 'c', 'c', 'c', 'c', 'Z'},
+ {2, 2, 6, 2, 6, 2, 6, 2, 6, 0, 2, 6, 6, 6, 6, 6, 6, 6, 6, 6, 0},
+ {0.0, -1.0, 0.0, 0.0, 0.5522848, 0.0, 1.0, 0.44771525, 1.0, 1.0, 0.0, 0.0, 0.0, 0.5522848, -0.44771525, 1.0, -1.0, 1.0, 0.0, 0.0, -0.5522848, 0.0, -1.0, -0.44771525, -1.0, -1.0, 0.0, 0.0, 0.0, -0.5522848, 0.44771525, -1.0, 1.0, -1.0, 7.0, -9.0, 0.0, 0.0, -14.0, 0.0, -14.0, 0.0, -1.1044922, 0.0, -2.0, 0.8955078, -2.0, 2.0, 0.0, 0.0, 0.0, 14.0, 0.0, 14.0, 0.0, 1.1044922, 0.8955078, 2.0, 2.0, 2.0, 0.0, 0.0, 14.0, 0.0, 14.0, 0.0, 1.1044922, 0.0, 2.0, -0.8955078, 2.0, -2.0, 0.0, 0.0, 0.0, -14.0, 0.0, -14.0, 0.0, -1.1044922, -0.8955078, -2.0, -2.0, -2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
+ },
+ [](SkPath* outPath) {
+ outPath->moveTo(0.0, -1.0);
+ outPath->rLineTo(0.0, 0.0);
+ outPath->rCubicTo(0.5522848, 0.0, 1.0, 0.44771525, 1.0, 1.0);
+ outPath->rLineTo(0.0, 0.0);
+ outPath->rCubicTo(0.0, 0.5522848, -0.44771525, 1.0, -1.0, 1.0);
+ outPath->rLineTo(0.0, 0.0);
+ outPath->rCubicTo(-0.5522848, 0.0, -1.0, -0.44771525, -1.0, -1.0);
+ outPath->rLineTo(0.0, 0.0);
+ outPath->rCubicTo(0.0, -0.5522848, 0.44771525, -1.0, 1.0, -1.0);
+ outPath->close();
+ outPath->moveTo(0.0, -1.0);
+ outPath->moveTo(7.0, -9.0);
+ outPath->rCubicTo(0.0, 0.0, -14.0, 0.0, -14.0, 0.0);
+ outPath->rCubicTo(-1.1044922, 0.0, -2.0, 0.8955078, -2.0, 2.0);
+ outPath->rCubicTo(0.0, 0.0, 0.0, 14.0, 0.0, 14.0);
+ outPath->rCubicTo(0.0, 1.1044922, 0.8955078, 2.0, 2.0, 2.0);
+ outPath->rCubicTo(0.0, 0.0, 14.0, 0.0, 14.0, 0.0);
+ outPath->rCubicTo(1.1044922, 0.0, 2.0, -0.8955078, 2.0, -2.0);
+ outPath->rCubicTo(0.0, 0.0, 0.0, -14.0, 0.0, -14.0);
+ outPath->rCubicTo(0.0, -1.1044922, -0.8955078, -2.0, -2.0, -2.0);
+ outPath->rCubicTo(0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
+ outPath->close();
+ outPath->moveTo(7.0, -9.0);
+ }
+ },
+
+ // pie1 in progress bar
+ {
+ "M300,70 a230,230 0 1,0 1,0 z",
+ {
+ {'M', 'a', 'z', },
+ {2, 7, 0, },
+ {300.0, 70.0, 230.0, 230.0, 0.0, 1.0, 0.0, 1.0, 0.0, },
+ },
+ [](SkPath* outPath) {
+ outPath->moveTo(300.0, 70.0);
+ outPath->cubicTo(239.06697794203706, 70.13246340443499, 180.6164396449267, 94.47383115953485, 137.6004913602211, 137.6302781499585);
+ outPath->cubicTo(94.58454307551551, 180.78672514038215, 70.43390412842275, 239.3163266242308, 70.50013586976587, 300.2494566687817);
+ outPath->cubicTo(70.56636761110899, 361.1825867133326, 94.84418775550249, 419.65954850554147, 137.9538527586204, 462.72238058830936);
+ outPath->cubicTo(181.06351776173827, 505.7852126710772, 239.5668339599056, 529.999456521097, 300.49999999999994, 529.999456521097);
+ outPath->cubicTo(361.43316604009436, 529.999456521097, 419.93648223826176, 505.78521267107726, 463.0461472413797, 462.7223805883093);
+ outPath->cubicTo(506.1558122444976, 419.65954850554135, 530.433632388891, 361.1825867133324, 530.4998641302341, 300.2494566687815);
+ outPath->cubicTo(530.5660958715771, 239.31632662423056, 506.4154569244844, 180.7867251403819, 463.3995086397787, 137.6302781499583);
+ outPath->cubicTo(420.383560355073, 94.47383115953468, 361.93302205796255, 70.13246340443492, 300.9999999999996, 70.00000000000003);
+ outPath->close();
+ outPath->moveTo(300.0, 70.0);
+ }
+ },
+
// Random long data
{
// Path
@@ -178,6 +238,7 @@
{"1-2e34567", false}
};
+
static bool hasSameVerbs(const PathData& from, const PathData& to) {
return from.verbs == to.verbs && from.verbSizes == to.verbSizes;
}
@@ -284,6 +345,49 @@
}
}
+TEST(VectorDrawable, matrixScale) {
+ struct MatrixAndScale {
+ float buffer[9];
+ float matrixScale;
+ };
+ const MatrixAndScale sMatrixAndScales[] {
+ {
+ {1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f},
+ 1.0
+ },
+ {
+ {1.0f, 0.0f, 240.0f, 0.0f, 1.0f, 240.0f, 0.0f, 0.0f, 1.0f},
+ 1.0f,
+ },
+ {
+ {1.5f, 0.0f, 24.0f, 0.0f, 1.5f, 24.0f, 0.0f, 0.0f, 1.0f},
+ 1.5f,
+ },
+ {
+ {0.99999994f, 0.0f, 300.0f, 0.0f, 0.99999994f, 158.57864f, 0.0f, 0.0f, 1.0f},
+ 0.99999994f,
+ },
+ {
+ {0.7071067f, 0.7071067f, 402.5305f, -0.7071067f, 0.7071067f, 169.18524f, 0.0f, 0.0f, 1.0f},
+ 0.99999994f,
+ },
+ {
+ {0.0f, 0.9999999f, 482.5305f, -0.9999999f, 0.0f, 104.18525f, 0.0f, 0.0f, 1.0f},
+ 0.9999999f,
+ },
+ {
+ {-0.35810637f, -0.93368083f, 76.55821f, 0.93368083f, -0.35810637f, 89.538506f, 0.0f, 0.0f, 1.0f},
+ 1.0000001f,
+ },
+ };
+
+ for (MatrixAndScale matrixAndScale : sMatrixAndScales) {
+ SkMatrix matrix;
+ matrix.set9(matrixAndScale.buffer);
+ float actualMatrixScale = VectorDrawable::Path::getMatrixScale(matrix);
+ EXPECT_EQ(matrixAndScale.matrixScale, actualMatrixScale);
+ }
+}
}; // namespace uirenderer
}; // namespace android
diff --git a/libs/hwui/utils/VectorDrawableUtils.h b/libs/hwui/utils/VectorDrawableUtils.h
index 21c1cdc..b5ef510 100644
--- a/libs/hwui/utils/VectorDrawableUtils.h
+++ b/libs/hwui/utils/VectorDrawableUtils.h
@@ -17,7 +17,7 @@
#ifndef ANDROID_HWUI_VECTORDRAWABLE_UTILS_H
#define ANDROID_HWUI_VECTORDRAWABLE_UTILS_H
-#include "VectorDrawablePath.h"
+#include "VectorDrawable.h"
#include <cutils/compiler.h>
#include "SkPath.h"