summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/res/res/values/attrs.xml2
-rw-r--r--graphics/java/android/graphics/drawable/VectorDrawable.java233
-rw-r--r--tests/VectorDrawableTest/res/drawable/vector_drawable22.xml72
-rw-r--r--tests/VectorDrawableTest/res/drawable/vector_drawable23.xml86
-rw-r--r--tests/VectorDrawableTest/res/values/strings.xml1
-rw-r--r--tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawablePerformance.java6
6 files changed, 340 insertions, 60 deletions
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index e347302..d28f9de 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -4798,6 +4798,8 @@
<attr name="scaleX" />
<!-- The amount to scale the group on X coordinate -->
<attr name="scaleY" />
+ <!-- The alpha of the group (0 is transparent and 1 is opaque) -->
+ <attr name="alpha" />
</declare-styleable>
<!-- Defines the path used in Vector Drawables. -->
diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java
index afd529c..304502e 100644
--- a/graphics/java/android/graphics/drawable/VectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/VectorDrawable.java
@@ -40,6 +40,7 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
+import java.util.Stack;
/**
* This lets you create a drawable based on an XML vector graphic It can be
@@ -129,6 +130,8 @@ public class VectorDrawable extends Drawable {
private static final int LINEJOIN_ROUND = 1;
private static final int LINEJOIN_BEVEL = 2;
+ private static final boolean DBG_VECTOR_DRAWABLE = false;
+
private final VectorDrawableState mVectorState;
private int mAlpha = 0xFF;
@@ -279,12 +282,17 @@ public class VectorDrawable extends Drawable {
boolean noGroupTag = true;
boolean noPathTag = true;
- VGroup currentGroup = new VGroup();
+ // 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);
int eventType = parser.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT) {
if (eventType == XmlPullParser.START_TAG) {
final String tagName = parser.getName();
+ final VGroup currentGroup = groupStack.peek();
+
if (SHAPE_PATH.equals(tagName)) {
final VPath path = new VPath();
path.inflate(res, attrs, theme);
@@ -297,18 +305,24 @@ public class VectorDrawable extends Drawable {
pathRenderer.parseViewport(res, attrs);
noViewportTag = false;
} else if (SHAPE_GROUP.equals(tagName)) {
- currentGroup = new VGroup();
- currentGroup.inflate(res, attrs, theme);
- pathRenderer.mGroupList.add(currentGroup);
+ VGroup newChildGroup = new VGroup();
+ newChildGroup.inflate(res, attrs, theme);
+ currentGroup.mChildGroupList.add(newChildGroup);
+ groupStack.push(newChildGroup);
noGroupTag = false;
}
+ } else if (eventType == XmlPullParser.END_TAG) {
+ final String tagName = parser.getName();
+ if (SHAPE_GROUP.equals(tagName)) {
+ groupStack.pop();
+ }
}
-
eventType = parser.next();
}
- if (noGroupTag && !noPathTag) {
- pathRenderer.mGroupList.add(currentGroup);
+ // Print the tree out for debug.
+ if (DBG_VECTOR_DRAWABLE) {
+ printGroupTree(pathRenderer.mRootGroup, 0);
}
if (noSizeTag || noViewportTag || noPathTag) {
@@ -338,6 +352,21 @@ public class VectorDrawable extends Drawable {
return pathRenderer;
}
+ private void printGroupTree(VGroup currentGroup, int level) {
+ String indent = "";
+ for (int i = 0 ; i < level ; i++) {
+ indent += " ";
+ }
+ // Print the current node
+ Log.v(LOGTAG, indent + "current group is :" + currentGroup.getName()
+ + " rotation is " + currentGroup.mRotate);
+ Log.v(LOGTAG, indent + "matrix is :" + currentGroup.getLocalMatrix().toString());
+ // Then print all the children
+ for (int i = 0 ; i < currentGroup.mChildGroupList.size(); i++) {
+ printGroupTree(currentGroup.mChildGroupList.get(i), level + 1);
+ }
+ }
+
private void setPathRenderer(VPathRenderer pathRenderer) {
mVectorState.mVPathRenderer = pathRenderer;
}
@@ -350,6 +379,7 @@ public class VectorDrawable extends Drawable {
public VectorDrawableState(VectorDrawableState copy) {
if (copy != null) {
mChangingConfigurations = copy.mChangingConfigurations;
+ // TODO: Make sure the constant state are handled correctly.
mVPathRenderer = new VPathRenderer(copy.mVPathRenderer);
mPadding = new Rect(copy.mPadding);
}
@@ -377,28 +407,42 @@ public class VectorDrawable extends Drawable {
}
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
+ *
+ */
+ private final VGroup mRootGroup;
+
private final Path mPath = new Path();
private final Path mRenderPath = new Path();
- private final Matrix mMatrix = new Matrix();
+ private static final Matrix IDENTITY_MATRIX = new Matrix();
private Paint mStrokePaint;
private Paint mFillPaint;
private ColorFilter mColorFilter;
private PathMeasure mPathMeasure;
- final ArrayList<VGroup> mGroupList = new ArrayList<VGroup>();
+ private float mBaseWidth = 0;
+ private float mBaseHeight = 0;
+ private float mViewportWidth = 0;
+ private float mViewportHeight = 0;
- float mBaseWidth = 0;
- float mBaseHeight = 0;
- float mViewportWidth = 0;
- float mViewportHeight = 0;
+ private final Matrix mFinalPathMatrix = new Matrix();
public VPathRenderer() {
+ mRootGroup = new VGroup();
}
public VPathRenderer(VPathRenderer copy) {
- mGroupList.addAll(copy.mGroupList);
-
+ mRootGroup = copy.mRootGroup;
mBaseWidth = copy.mBaseWidth;
mBaseHeight = copy.mBaseHeight;
mViewportWidth = copy.mViewportHeight;
@@ -406,33 +450,59 @@ public class VectorDrawable extends Drawable {
}
public boolean canApplyTheme() {
- final ArrayList<VGroup> groups = mGroupList;
- for (int i = groups.size() - 1; i >= 0; i--) {
- final ArrayList<VPath> paths = groups.get(i).mVGList;
- for (int j = paths.size() - 1; j >= 0; j--) {
- final VPath path = paths.get(j);
- if (path.canApplyTheme()) {
- return true;
- }
+ // If one of the paths can apply theme, then return true;
+ return recursiveCanApplyTheme(mRootGroup);
+ }
+
+ private boolean recursiveCanApplyTheme(VGroup currentGroup) {
+ // We can do a tree traverse here, if there is one path return true,
+ // then we return true for the whole tree.
+ final ArrayList<VPath> paths = currentGroup.mPathList;
+ for (int j = paths.size() - 1; j >= 0; j--) {
+ final VPath path = paths.get(j);
+ if (path.canApplyTheme()) {
+ return true;
}
}
+ final ArrayList<VGroup> childGroups = currentGroup.mChildGroupList;
+
+ for (int i = 0; i < childGroups.size(); i++) {
+ VGroup childGroup = childGroups.get(i);
+ if (childGroup.canApplyTheme()
+ || recursiveCanApplyTheme(childGroup)) {
+ return true;
+ }
+ }
return false;
}
public void applyTheme(Theme t) {
- final ArrayList<VGroup> groups = mGroupList;
- for (int i = groups.size() - 1; i >= 0; i--) {
- VGroup currentGroup = groups.get(i);
- currentGroup.applyTheme(t);
- final ArrayList<VPath> paths = currentGroup.mVGList;
- for (int j = paths.size() - 1; j >= 0; j--) {
- final VPath path = paths.get(j);
- if (path.canApplyTheme()) {
- path.applyTheme(t);
- }
+ // Apply theme to every path of the tree.
+ recursiveApplyTheme(mRootGroup, t);
+ }
+
+ private void recursiveApplyTheme(VGroup currentGroup, Theme t) {
+ // We can do a tree traverse here, apply theme to all paths which
+ // can apply theme.
+ final ArrayList<VPath> paths = currentGroup.mPathList;
+ for (int j = paths.size() - 1; j >= 0; j--) {
+ final VPath path = paths.get(j);
+ if (path.canApplyTheme()) {
+ path.applyTheme(t);
}
}
+
+ final ArrayList<VGroup> childGroups = currentGroup.mChildGroupList;
+
+ for (int i = 0; i < childGroups.size(); i++) {
+ VGroup childGroup = childGroups.get(i);
+ if (childGroup.canApplyTheme()) {
+ childGroup.applyTheme(t);
+ }
+ recursiveApplyTheme(childGroup, t);
+ }
+
}
public void setColorFilter(ColorFilter colorFilter) {
@@ -448,34 +518,35 @@ public class VectorDrawable extends Drawable {
}
- public void draw(Canvas canvas, int w, int h) {
- if (mGroupList == null || mGroupList.size() == 0) {
- Log.e(LOGTAG,"There is no group to draw");
- return;
+ private void drawGroupTree(VGroup currentGroup, Matrix currentMatrix,
+ Canvas canvas, int w, int h) {
+ // 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.
+ currentGroup.mStackedMatrix.set(currentMatrix);
+
+ currentGroup.mStackedMatrix.preConcat(currentGroup.mLocalMatrix);
+
+ drawPath(currentGroup, canvas, w, h);
+ // Draw the group tree in post order.
+ for (int i = 0 ; i < currentGroup.mChildGroupList.size(); i++) {
+ drawGroupTree(currentGroup.mChildGroupList.get(i),
+ currentGroup.mStackedMatrix, canvas, w, h);
}
+ }
- for (int i = 0; i < mGroupList.size(); i++) {
- VGroup currentGroup = mGroupList.get(i);
- if (currentGroup != null) {
- drawPath(currentGroup, canvas, w, h);
- }
- }
+ public void draw(Canvas canvas, int w, int h) {
+ // Travese the tree in pre-order to draw.
+ drawGroupTree(mRootGroup, IDENTITY_MATRIX, canvas, w, h);
}
private void drawPath(VGroup vGroup, Canvas canvas, int w, int h) {
final float scale = Math.min(h / mViewportHeight, w / mViewportWidth);
- mMatrix.reset();
-
- // The order we apply is the same as the
- // RenderNode.cpp::applyViewPropertyTransforms().
- mMatrix.postTranslate(-vGroup.mPivotX, -vGroup.mPivotY);
- mMatrix.postScale(vGroup.mScaleX, vGroup.mScaleY);
- mMatrix.postRotate(vGroup.mRotate, 0, 0);
- mMatrix.postTranslate(vGroup.mTranslateX + vGroup.mPivotX, vGroup.mTranslateY + vGroup.mPivotY);
-
- mMatrix.postScale(scale, scale, mViewportWidth / 2f, mViewportHeight / 2f);
- mMatrix.postTranslate(w / 2f - mViewportWidth / 2f, h / 2f - mViewportHeight / 2f);
+ mFinalPathMatrix.set(vGroup.mStackedMatrix);
+ mFinalPathMatrix.postScale(scale, scale, mViewportWidth / 2f, mViewportHeight / 2f);
+ mFinalPathMatrix.postTranslate(w / 2f - mViewportWidth / 2f, h / 2f - mViewportHeight / 2f);
ArrayList<VPath> paths = vGroup.getPaths();
for (int i = 0; i < paths.size(); i++) {
@@ -507,7 +578,7 @@ public class VectorDrawable extends Drawable {
mRenderPath.reset();
- mRenderPath.addPath(path, mMatrix);
+ mRenderPath.addPath(path, mFinalPathMatrix);
if (vPath.mClip) {
canvas.clipPath(mRenderPath, Region.Op.REPLACE);
@@ -588,7 +659,8 @@ public class VectorDrawable extends Drawable {
private static class VGroup {
private final HashMap<String, VPath> mVGPathMap = new HashMap<String, VPath>();
- private final ArrayList<VPath> mVGList = new ArrayList<VPath>();
+ private final ArrayList<VPath> mPathList = new ArrayList<VPath>();
+ private final ArrayList<VGroup> mChildGroupList = new ArrayList<VGroup>();
private float mRotate = 0;
private float mPivotX = 0;
@@ -597,15 +669,36 @@ public class VectorDrawable extends Drawable {
private float mScaleY = 1;
private float mTranslateX = 0;
private float mTranslateY = 0;
+ private float mAlpha = 1;
+
+ // mLocalMatrix is parsed from the XML.
+ private final Matrix mLocalMatrix = new Matrix();
+ // mStackedMatrix is only used when drawing, it combines all the
+ // parents' local matrices with the current one.
+ private final Matrix mStackedMatrix = new Matrix();
private int[] mThemeAttrs;
+ private String mName = null;
+
+ public String getName() {
+ return mName;
+ }
+
+ public Matrix getLocalMatrix() {
+ return mLocalMatrix;
+ }
+
public void add(VPath path) {
String id = path.getID();
mVGPathMap.put(id, path);
- mVGList.add(path);
+ mPathList.add(path);
}
+ public boolean canApplyTheme() {
+ return mThemeAttrs != null;
+ }
+
public void applyTheme(Theme t) {
if (mThemeAttrs == null) {
return;
@@ -621,6 +714,11 @@ public class VectorDrawable extends Drawable {
mScaleY = a.getFloat(R.styleable.VectorDrawableGroup_scaleY, mScaleY);
mTranslateX = a.getFloat(R.styleable.VectorDrawableGroup_translateX, mTranslateX);
mTranslateY = a.getFloat(R.styleable.VectorDrawableGroup_translateY, mTranslateY);
+ mAlpha = a.getFloat(R.styleable.VectorDrawableGroup_alpha, mAlpha);
+ updateLocalMatrix();
+ if (a.hasValue(R.styleable.VectorDrawableGroup_name)) {
+ mName = a.getString(R.styleable.VectorDrawableGroup_name);
+ }
a.recycle();
}
@@ -660,15 +758,34 @@ public class VectorDrawable extends Drawable {
mTranslateY = a.getFloat(R.styleable.VectorDrawableGroup_translateY, mTranslateY);
}
+ if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawableGroup_name] == 0) {
+ mName = a.getString(R.styleable.VectorDrawableGroup_name);
+ }
+
+ if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawableGroup_alpha] == 0) {
+ mAlpha = a.getFloat(R.styleable.VectorDrawableGroup_alpha, mAlpha);
+ }
+
+ updateLocalMatrix();
a.recycle();
}
+ 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);
+ }
+
/**
* Must return in order of adding
* @return ordered list of paths
*/
public ArrayList<VPath> getPaths() {
- return mVGList;
+ return mPathList;
}
}
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable22.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable22.xml
new file mode 100644
index 0000000..8d38cb5
--- /dev/null
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable22.xml
@@ -0,0 +1,72 @@
+<!--
+ Copyright (C) 2014 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+
+ <size
+ android:height="64dp"
+ android:width="64dp" />
+
+ <viewport
+ android:viewportHeight="400"
+ android:viewportWidth="400" />
+
+ <group android:name="backgroundGroup" >
+ <path
+ android:name="background1"
+ android:fill="#80000000"
+ android:pathData="M 0,0 l 200,0 l 0, 200 l -200, 0 z" />
+ <path
+ android:name="background2"
+ android:fill="#80000000"
+ android:pathData="M 200,200 l 200,0 l 0, 200 l -200, 0 z" />
+ </group>
+ <group
+ android:name="translateToCenterGroup"
+ android:translateX="50.0"
+ android:translateY="90.0" >
+ <path
+ android:name="twoLines"
+ android:pathData="M 0,0 v 100 M 0,0 h 100"
+ android:stroke="#FFFF0000"
+ android:strokeWidth="20" />
+
+ <group
+ android:name="rotationGroup"
+ android:pivotX="0.0"
+ android:pivotY="0.0"
+ android:rotation="-45.0" >
+ <path
+ android:name="twoLines1"
+ android:pathData="M 0,0 v 100 M 0,0 h 100"
+ android:stroke="#FF00FF00"
+ android:strokeWidth="20" />
+
+ <group
+ android:name="translateGroup"
+ android:translateX="130.0"
+ android:translateY="160.0" >
+ <group android:name="scaleGroup" >
+ <path
+ android:name="twoLines2"
+ android:pathData="M 0,0 v 100 M 0,0 h 100"
+ android:stroke="#FF0000FF"
+ android:strokeWidth="20" />
+ </group>
+ </group>
+ </group>
+ </group>
+
+</vector> \ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable23.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable23.xml
new file mode 100644
index 0000000..52acd7a
--- /dev/null
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable23.xml
@@ -0,0 +1,86 @@
+<!--
+ Copyright (C) 2014 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+
+ <size
+ android:height="64dp"
+ android:width="64dp" />
+
+ <viewport
+ android:viewportHeight="400"
+ android:viewportWidth="400" />
+
+ <group android:name="backgroundGroup" >
+ <path
+ android:name="background1"
+ android:fill="#80000000"
+ android:pathData="M 0,0 l 200,0 l 0, 200 l -200, 0 z" />
+ <path
+ android:name="background2"
+ android:fill="#80000000"
+ android:pathData="M 200,200 l 200,0 l 0, 200 l -200, 0 z" />
+ </group>
+ <group
+ android:name="translateToCenterGroup"
+ android:translateX="50.0"
+ android:translateY="90.0" >
+ <path
+ android:name="twoLines"
+ android:pathData="@string/twoLinePathData"
+ android:stroke="#FFFF0000"
+ android:strokeWidth="20" />
+
+ <group
+ android:name="rotationGroup"
+ android:pivotX="0.0"
+ android:pivotY="0.0"
+ android:rotation="-45.0" >
+ <path
+ android:name="twoLines1"
+ android:pathData="@string/twoLinePathData"
+ android:stroke="#FF00FF00"
+ android:strokeWidth="20" />
+
+ <group
+ android:name="translateGroup"
+ android:translateX="130.0"
+ android:translateY="160.0" >
+ <group android:name="scaleGroup" >
+ <path
+ android:name="twoLines3"
+ android:pathData="@string/twoLinePathData"
+ android:stroke="#FF0000FF"
+ android:strokeWidth="20" />
+ </group>
+ </group>
+
+ <group
+ android:name="translateGroupHalf"
+ android:translateX="65.0"
+ android:translateY="80.0" >
+ <group android:name="scaleGroup" >
+ <path
+ android:name="twoLines2"
+ android:pathData="@string/twoLinePathData"
+ android:fill="?android:attr/colorForeground"
+ android:stroke="?android:attr/colorForeground"
+ android:strokeWidth="20" />
+ </group>
+ </group>
+ </group>
+ </group>
+
+</vector> \ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/values/strings.xml b/tests/VectorDrawableTest/res/values/strings.xml
index 64163c2..b49a1aa 100644
--- a/tests/VectorDrawableTest/res/values/strings.xml
+++ b/tests/VectorDrawableTest/res/values/strings.xml
@@ -15,4 +15,5 @@
-->
<resources>
+ <string name="twoLinePathData" >"M 0,0 v 100 M 0,0 h 100"</string>
</resources>
diff --git a/tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawablePerformance.java b/tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawablePerformance.java
index e0624e5..c2a5e6b 100644
--- a/tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawablePerformance.java
+++ b/tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawablePerformance.java
@@ -17,11 +17,11 @@ import android.app.Activity;
import android.content.res.Resources;
import android.graphics.drawable.VectorDrawable;
import android.os.Bundle;
-import android.view.View;
import android.widget.TextView;
import android.widget.Button;
import android.widget.GridLayout;
import android.widget.ScrollView;
+
import java.text.DecimalFormat;
@SuppressWarnings({"UnusedDeclaration"})
@@ -48,7 +48,9 @@ public class VectorDrawablePerformance extends Activity {
R.drawable.vector_drawable18,
R.drawable.vector_drawable19,
R.drawable.vector_drawable20,
- R.drawable.vector_drawable21
+ R.drawable.vector_drawable21,
+ R.drawable.vector_drawable22,
+ R.drawable.vector_drawable23
};
@Override