VectorDrawable.java revision 4a81674b45b7250c4e2a80330371f7aa1c066d05
1e6b680364dd992907a8d2037685a2e500d188dfbDianne Hackborn/*
2e6b680364dd992907a8d2037685a2e500d188dfbDianne Hackborn * Copyright (C) 2014 The Android Open Source Project
3e6b680364dd992907a8d2037685a2e500d188dfbDianne Hackborn *
4e6b680364dd992907a8d2037685a2e500d188dfbDianne Hackborn * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5e6b680364dd992907a8d2037685a2e500d188dfbDianne Hackborn * in compliance with the License. You may obtain a copy of the License at
6e6b680364dd992907a8d2037685a2e500d188dfbDianne Hackborn *
7e6b680364dd992907a8d2037685a2e500d188dfbDianne Hackborn * http://www.apache.org/licenses/LICENSE-2.0
8e6b680364dd992907a8d2037685a2e500d188dfbDianne Hackborn *
9e6b680364dd992907a8d2037685a2e500d188dfbDianne Hackborn * Unless required by applicable law or agreed to in writing, software distributed under the License
10fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * or implied. See the License for the specific language governing permissions and limitations under
12fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * the License.
13fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski */
14fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski
15fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinskipackage android.graphics.drawable;
16fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski
17fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinskiimport android.annotation.NonNull;
18e6b680364dd992907a8d2037685a2e500d188dfbDianne Hackbornimport android.annotation.Nullable;
19fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinskiimport android.content.pm.ActivityInfo.Config;
20fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinskiimport android.content.res.ColorStateList;
21fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinskiimport android.content.res.ComplexColor;
22fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinskiimport android.content.res.GradientColor;
23fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinskiimport android.content.res.Resources;
24fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinskiimport android.content.res.Resources.Theme;
25e6b680364dd992907a8d2037685a2e500d188dfbDianne Hackbornimport android.content.res.TypedArray;
26e6b680364dd992907a8d2037685a2e500d188dfbDianne Hackbornimport android.graphics.Canvas;
27e6b680364dd992907a8d2037685a2e500d188dfbDianne Hackbornimport android.graphics.ColorFilter;
28e6b680364dd992907a8d2037685a2e500d188dfbDianne Hackbornimport android.graphics.Insets;
29e6b680364dd992907a8d2037685a2e500d188dfbDianne Hackbornimport android.graphics.PixelFormat;
30fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinskiimport android.graphics.PorterDuff.Mode;
31e6b680364dd992907a8d2037685a2e500d188dfbDianne Hackbornimport android.graphics.PorterDuffColorFilter;
32fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinskiimport android.graphics.Rect;
33fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinskiimport android.graphics.Shader;
34fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinskiimport android.util.ArrayMap;
35fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinskiimport android.util.AttributeSet;
36fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinskiimport android.util.DisplayMetrics;
37fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinskiimport android.util.FloatProperty;
38fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinskiimport android.util.IntProperty;
39fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinskiimport android.util.LayoutDirection;
40fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinskiimport android.util.Log;
41fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinskiimport android.util.PathParser;
42fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinskiimport android.util.Property;
43fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinskiimport android.util.Xml;
44fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski
45fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinskiimport com.android.internal.R;
46fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinskiimport com.android.internal.util.VirtualRefBasePtr;
47fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski
48fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinskiimport org.xmlpull.v1.XmlPullParser;
49fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinskiimport org.xmlpull.v1.XmlPullParserException;
50fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski
51e6b680364dd992907a8d2037685a2e500d188dfbDianne Hackbornimport java.io.IOException;
52e6b680364dd992907a8d2037685a2e500d188dfbDianne Hackbornimport java.nio.ByteBuffer;
53fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinskiimport java.nio.ByteOrder;
54fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinskiimport java.util.ArrayList;
55fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinskiimport java.util.HashMap;
56788fa41482b9d398591b7db8b0b01839029611adNarayan Kamathimport java.util.Stack;
57a2ef5c0d4fb863c0382e77ae00f986a019b11cbeAnton Krumin
58a2ef5c0d4fb863c0382e77ae00f986a019b11cbeAnton Kruminimport dalvik.annotation.optimization.FastNative;
59e6b680364dd992907a8d2037685a2e500d188dfbDianne Hackbornimport dalvik.system.VMRuntime;
60e6b680364dd992907a8d2037685a2e500d188dfbDianne Hackborn
61fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski/**
62fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * This lets you create a drawable based on an XML vector graphic.
63fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * <p/>
64fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * <strong>Note:</strong> To optimize for the re-drawing performance, one bitmap cache is created
65fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * for each VectorDrawable. Therefore, referring to the same VectorDrawable means sharing the same
66fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * bitmap cache. If these references don't agree upon on the same size, the bitmap will be recreated
67fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * and redrawn every time size is changed. In other words, if a VectorDrawable is used for
68fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * different sizes, it is more efficient to create multiple VectorDrawables, one for each size.
69fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * <p/>
70fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * VectorDrawable can be defined in an XML file with the <code>&lt;vector></code> element.
71fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * <p/>
72fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * The vector drawable has the following elements:
73fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * <p/>
74fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * <dt><code>&lt;vector></code></dt>
75fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * <dl>
76fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * <dd>Used to define a vector drawable
77fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * <dl>
78fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * <dt><code>android:name</code></dt>
79fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * <dd>Defines the name of this vector drawable.</dd>
80fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * <dt><code>android:width</code></dt>
81fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * <dd>Used to define the intrinsic width of the drawable.
82fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * This support all the dimension units, normally specified with dp.</dd>
83fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * <dt><code>android:height</code></dt>
84fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * <dd>Used to define the intrinsic height the drawable.
85fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * This support all the dimension units, normally specified with dp.</dd>
86fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * <dt><code>android:viewportWidth</code></dt>
87fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * <dd>Used to define the width of the viewport space. Viewport is basically
88fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * the virtual canvas where the paths are drawn on.</dd>
89fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * <dt><code>android:viewportHeight</code></dt>
90fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * <dd>Used to define the height of the viewport space. Viewport is basically
91fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * the virtual canvas where the paths are drawn on.</dd>
92fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * <dt><code>android:tint</code></dt>
93fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * <dd>The color to apply to the drawable as a tint. By default, no tint is applied.</dd>
94fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * <dt><code>android:tintMode</code></dt>
95fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * <dd>The Porter-Duff blending mode for the tint color. Default is src_in.</dd>
96fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * <dt><code>android:autoMirrored</code></dt>
97fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * <dd>Indicates if the drawable needs to be mirrored when its layout direction is
98fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * RTL (right-to-left). Default is false.</dd>
99fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * <dt><code>android:alpha</code></dt>
100fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * <dd>The opacity of this drawable. Default is 1.0.</dd>
101fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * </dl></dd>
102fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * </dl>
103fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski *
104fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * <dl>
105fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * <dt><code>&lt;group></code></dt>
106fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * <dd>Defines a group of paths or subgroups, plus transformation information.
107fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * The transformations are defined in the same coordinates as the viewport.
108fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * And the transformations are applied in the order of scale, rotate then translate.
109fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * <dl>
110fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * <dt><code>android:name</code></dt>
111fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * <dd>Defines the name of the group.</dd>
112fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * <dt><code>android:rotation</code></dt>
113fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * <dd>The degrees of rotation of the group. Default is 0.</dd>
114fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * <dt><code>android:pivotX</code></dt>
115fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * <dd>The X coordinate of the pivot for the scale and rotation of the group.
116fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * This is defined in the viewport space. Default is 0.</dd>
117fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * <dt><code>android:pivotY</code></dt>
118fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * <dd>The Y coordinate of the pivot for the scale and rotation of the group.
119fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * This is defined in the viewport space. Default is 0.</dd>
120fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * <dt><code>android:scaleX</code></dt>
121fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * <dd>The amount of scale on the X Coordinate. Default is 1.</dd>
122fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * <dt><code>android:scaleY</code></dt>
123fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * <dd>The amount of scale on the Y coordinate. Default is 1.</dd>
124fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * <dt><code>android:translateX</code></dt>
125fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * <dd>The amount of translation on the X coordinate.
126fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * This is defined in the viewport space. Default is 0.</dd>
127fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * <dt><code>android:translateY</code></dt>
128fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * <dd>The amount of translation on the Y coordinate.
129fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * This is defined in the viewport space. Default is 0.</dd>
130fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * </dl></dd>
131fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * </dl>
132fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski *
133fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * <dl>
134fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * <dt><code>&lt;path></code></dt>
135fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * <dd>Defines paths to be drawn.
136fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * <dl>
137fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * <dt><code>android:name</code></dt>
138fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * <dd>Defines the name of the path.</dd>
139fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * <dt><code>android:pathData</code></dt>
140fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * <dd>Defines path data using exactly same format as "d" attribute
141fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * in the SVG's path data. This is defined in the viewport space.</dd>
142fab50875b98e8274ac8ee44b38ba42521bbbf1f9Adam Lesinski * <dt><code>android:fillColor</code></dt>
143e6b680364dd992907a8d2037685a2e500d188dfbDianne Hackborn * <dd>Specifies the color used to fill the path. May be a color or, for SDK 24+, a color state list
144 * or a gradient color (See {@link android.R.styleable#GradientColor}
145 * and {@link android.R.styleable#GradientColorItem}).
146 * If this property is animated, any value set by the animation will override the original value.
147 * No path fill is drawn if this property is not specified.</dd>
148 * <dt><code>android:strokeColor</code></dt>
149 * <dd>Specifies the color used to draw the path outline. May be a color or, for SDK 24+, a color
150 * state list or a gradient color (See {@link android.R.styleable#GradientColor}
151 * and {@link android.R.styleable#GradientColorItem}).
152 * If this property is animated, any value set by the animation will override the original value.
153 * No path outline is drawn if this property is not specified.</dd>
154 * <dt><code>android:strokeWidth</code></dt>
155 * <dd>The width a path stroke. Default is 0.</dd>
156 * <dt><code>android:strokeAlpha</code></dt>
157 * <dd>The opacity of a path stroke. Default is 1.</dd>
158 * <dt><code>android:fillAlpha</code></dt>
159 * <dd>The opacity to fill the path with. Default is 1.</dd>
160 * <dt><code>android:trimPathStart</code></dt>
161 * <dd>The fraction of the path to trim from the start, in the range from 0 to 1. Default is 0.</dd>
162 * <dt><code>android:trimPathEnd</code></dt>
163 * <dd>The fraction of the path to trim from the end, in the range from 0 to 1. Default is 1.</dd>
164 * <dt><code>android:trimPathOffset</code></dt>
165 * <dd>Shift trim region (allows showed region to include the start and end), in the range
166 * from 0 to 1. Default is 0.</dd>
167 * <dt><code>android:strokeLineCap</code></dt>
168 * <dd>Sets the linecap for a stroked path: butt, round, square. Default is butt.</dd>
169 * <dt><code>android:strokeLineJoin</code></dt>
170 * <dd>Sets the lineJoin for a stroked path: miter,round,bevel. Default is miter.</dd>
171 * <dt><code>android:strokeMiterLimit</code></dt>
172 * <dd>Sets the Miter limit for a stroked path. Default is 4.</dd>
173 * <dt><code>android:fillType</code></dt>
174 * <dd>For SDK 24+, sets the fillType for a path. The types can be either "evenOdd" or "nonZero". They behave the
175 * same as SVG's "fill-rule" properties. Default is nonZero. For more details, see
176 * <a href="https://www.w3.org/TR/SVG/painting.html#FillRuleProperty">FillRuleProperty</a></dd>
177 * </dl></dd>
178 *
179 * </dl>
180 *
181 * <dl>
182 * <dt><code>&lt;clip-path></code></dt>
183 * <dd>Defines path to be the current clip. Note that the clip path only apply to
184 * the current group and its children.
185 * <dl>
186 * <dt><code>android:name</code></dt>
187 * <dd>Defines the name of the clip path.</dd>
188 * <dd>Animatable : No.</dd>
189 * <dt><code>android:pathData</code></dt>
190 * <dd>Defines clip path using the same format as "d" attribute
191 * in the SVG's path data.</dd>
192 * <dd>Animatable : Yes.</dd>
193 * </dl></dd>
194 * </dl>
195 * <li>Here is a simple VectorDrawable in this vectordrawable.xml file.
196 * <pre>
197 * &lt;vector xmlns:android=&quot;http://schemas.android.com/apk/res/android";
198 *     android:height=&quot;64dp&quot;
199 *     android:width=&quot;64dp&quot;
200 *     android:viewportHeight=&quot;600&quot;
201 *     android:viewportWidth=&quot;600&quot; &gt;
202 *     &lt;group
203 *         android:name=&quot;rotationGroup&quot;
204 *         android:pivotX=&quot;300.0&quot;
205 *         android:pivotY=&quot;300.0&quot;
206 *         android:rotation=&quot;45.0&quot; &gt;
207 *         &lt;path
208 *             android:name=&quot;v&quot;
209 *             android:fillColor=&quot;#000000&quot;
210 *             android:pathData=&quot;M300,70 l 0,-70 70,70 0,0 -70,70z&quot; /&gt;
211 *     &lt;/group&gt;
212 * &lt;/vector&gt;
213 * </pre>
214 * </li>
215 * <li>And here is an example of linear gradient color, which is supported in SDK 24+.
216 * See more details in {@link android.R.styleable#GradientColor} and
217 * {@link android.R.styleable#GradientColorItem}.
218 * <pre>
219 * &lt;gradient xmlns:android="http://schemas.android.com/apk/res/android"
220 *     android:angle="90"
221 *     android:startColor="?android:attr/colorPrimary"
222 *     android:endColor="?android:attr/colorControlActivated"
223 *     android:centerColor="#f00"
224 *     android:startX="0"
225 *     android:startY="0"
226 *     android:endX="100"
227 *     android:endY="100"
228 *     android:type="linear"&gt;
229 * &lt;/gradient&gt;
230 * </pre>
231 * </li>
232 *
233 */
234
235public class VectorDrawable extends Drawable {
236    private static final String LOGTAG = VectorDrawable.class.getSimpleName();
237
238    private static final String SHAPE_CLIP_PATH = "clip-path";
239    private static final String SHAPE_GROUP = "group";
240    private static final String SHAPE_PATH = "path";
241    private static final String SHAPE_VECTOR = "vector";
242
243    private VectorDrawableState mVectorState;
244
245    private PorterDuffColorFilter mTintFilter;
246    private ColorFilter mColorFilter;
247
248    private boolean mMutated;
249
250    /** The density of the display on which this drawable will be rendered. */
251    private int mTargetDensity;
252
253    // Given the virtual display setup, the dpi can be different than the inflation's dpi.
254    // Therefore, we need to scale the values we got from the getDimension*().
255    private int mDpiScaledWidth = 0;
256    private int mDpiScaledHeight = 0;
257    private Insets mDpiScaledInsets = Insets.NONE;
258
259    /** Whether DPI-scaled width, height, and insets need to be updated. */
260    private boolean mDpiScaledDirty = true;
261
262    // Temp variable, only for saving "new" operation at the draw() time.
263    private final Rect mTmpBounds = new Rect();
264
265    public VectorDrawable() {
266        this(new VectorDrawableState(null), null);
267    }
268
269    /**
270     * The one constructor to rule them all. This is called by all public
271     * constructors to set the state and initialize local properties.
272     */
273    private VectorDrawable(@NonNull VectorDrawableState state, @Nullable Resources res) {
274        mVectorState = state;
275        updateLocalState(res);
276    }
277
278    /**
279     * Initializes local dynamic properties from state. This should be called
280     * after significant state changes, e.g. from the One True Constructor and
281     * after inflating or applying a theme.
282     *
283     * @param res resources of the context in which the drawable will be
284     *            displayed, or {@code null} to use the constant state defaults
285     */
286    private void updateLocalState(Resources res) {
287        final int density = Drawable.resolveDensity(res, mVectorState.mDensity);
288        if (mTargetDensity != density) {
289            mTargetDensity = density;
290            mDpiScaledDirty = true;
291        }
292
293        mTintFilter = updateTintFilter(mTintFilter, mVectorState.mTint, mVectorState.mTintMode);
294    }
295
296    @Override
297    public Drawable mutate() {
298        if (!mMutated && super.mutate() == this) {
299            mVectorState = new VectorDrawableState(mVectorState);
300            mMutated = true;
301        }
302        return this;
303    }
304
305    /**
306     * @hide
307     */
308    public void clearMutated() {
309        super.clearMutated();
310        mMutated = false;
311    }
312
313    Object getTargetByName(String name) {
314        return mVectorState.mVGTargetsMap.get(name);
315    }
316
317    @Override
318    public ConstantState getConstantState() {
319        mVectorState.mChangingConfigurations = getChangingConfigurations();
320        return mVectorState;
321    }
322
323    @Override
324    public void draw(Canvas canvas) {
325        // We will offset the bounds for drawBitmap, so copyBounds() here instead
326        // of getBounds().
327        copyBounds(mTmpBounds);
328        if (mTmpBounds.width() <= 0 || mTmpBounds.height() <= 0) {
329            // Nothing to draw
330            return;
331        }
332
333        // Color filters always override tint filters.
334        final ColorFilter colorFilter = (mColorFilter == null ? mTintFilter : mColorFilter);
335        final long colorFilterNativeInstance = colorFilter == null ? 0 :
336                colorFilter.getNativeInstance();
337        boolean canReuseCache = mVectorState.canReuseCache();
338        int pixelCount = nDraw(mVectorState.getNativeRenderer(), canvas.getNativeCanvasWrapper(),
339                colorFilterNativeInstance, mTmpBounds, needMirroring(),
340                canReuseCache);
341        if (pixelCount == 0) {
342            // Invalid canvas matrix or drawable bounds. This would not affect existing bitmap
343            // cache, if any.
344            return;
345        }
346
347        int deltaInBytes;
348        // Track different bitmap cache based whether the canvas is hw accelerated. By doing so,
349        // we don't over count bitmap cache allocation: if the input canvas is always of the same
350        // type, only one bitmap cache is allocated.
351        if (canvas.isHardwareAccelerated()) {
352            // Each pixel takes 4 bytes.
353            deltaInBytes = (pixelCount - mVectorState.mLastHWCachePixelCount) * 4;
354            mVectorState.mLastHWCachePixelCount = pixelCount;
355        } else {
356            // Each pixel takes 4 bytes.
357            deltaInBytes = (pixelCount - mVectorState.mLastSWCachePixelCount) * 4;
358            mVectorState.mLastSWCachePixelCount = pixelCount;
359        }
360        if (deltaInBytes > 0) {
361            VMRuntime.getRuntime().registerNativeAllocation(deltaInBytes);
362        } else if (deltaInBytes < 0) {
363            VMRuntime.getRuntime().registerNativeFree(-deltaInBytes);
364        }
365    }
366
367
368    @Override
369    public int getAlpha() {
370        return (int) (mVectorState.getAlpha() * 255);
371    }
372
373    @Override
374    public void setAlpha(int alpha) {
375        if (mVectorState.setAlpha(alpha / 255f)) {
376            invalidateSelf();
377        }
378    }
379
380    @Override
381    public void setColorFilter(ColorFilter colorFilter) {
382        mColorFilter = colorFilter;
383        invalidateSelf();
384    }
385
386    @Override
387    public ColorFilter getColorFilter() {
388        return mColorFilter;
389    }
390
391    @Override
392    public void setTintList(ColorStateList tint) {
393        final VectorDrawableState state = mVectorState;
394        if (state.mTint != tint) {
395            state.mTint = tint;
396            mTintFilter = updateTintFilter(mTintFilter, tint, state.mTintMode);
397            invalidateSelf();
398        }
399    }
400
401    @Override
402    public void setTintMode(Mode tintMode) {
403        final VectorDrawableState state = mVectorState;
404        if (state.mTintMode != tintMode) {
405            state.mTintMode = tintMode;
406            mTintFilter = updateTintFilter(mTintFilter, state.mTint, tintMode);
407            invalidateSelf();
408        }
409    }
410
411    @Override
412    public boolean isStateful() {
413        return super.isStateful() || (mVectorState != null && mVectorState.isStateful());
414    }
415
416    /** @hide */
417    @Override
418    public boolean hasFocusStateSpecified() {
419        return mVectorState != null && mVectorState.hasFocusStateSpecified();
420    }
421
422    @Override
423    protected boolean onStateChange(int[] stateSet) {
424        boolean changed = false;
425
426        // When the VD is stateful, we need to mutate the drawable such that we don't share the
427        // cache bitmap with others. Such that the state change only affect this new cached bitmap.
428        if (isStateful()) {
429            mutate();
430        }
431        final VectorDrawableState state = mVectorState;
432        if (state.onStateChange(stateSet)) {
433            changed = true;
434            state.mCacheDirty = true;
435        }
436        if (state.mTint != null && state.mTintMode != null) {
437            mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
438            changed = true;
439        }
440
441        return changed;
442    }
443
444    @Override
445    public int getOpacity() {
446        // We can't tell whether the drawable is fully opaque unless we examine all the pixels,
447        // but we could tell it is transparent if the root alpha is 0.
448        return getAlpha() == 0 ? PixelFormat.TRANSPARENT : PixelFormat.TRANSLUCENT;
449    }
450
451    @Override
452    public int getIntrinsicWidth() {
453        if (mDpiScaledDirty) {
454            computeVectorSize();
455        }
456        return mDpiScaledWidth;
457    }
458
459    @Override
460    public int getIntrinsicHeight() {
461        if (mDpiScaledDirty) {
462            computeVectorSize();
463        }
464        return mDpiScaledHeight;
465    }
466
467    /** @hide */
468    @Override
469    public Insets getOpticalInsets() {
470        if (mDpiScaledDirty) {
471            computeVectorSize();
472        }
473        return mDpiScaledInsets;
474    }
475
476    /*
477     * Update local dimensions to adjust for a target density that may differ
478     * from the source density against which the constant state was loaded.
479     */
480    void computeVectorSize() {
481        final Insets opticalInsets = mVectorState.mOpticalInsets;
482
483        final int sourceDensity = mVectorState.mDensity;
484        final int targetDensity = mTargetDensity;
485        if (targetDensity != sourceDensity) {
486            mDpiScaledWidth = Drawable.scaleFromDensity(
487                    (int) mVectorState.mBaseWidth, sourceDensity, targetDensity, true);
488            mDpiScaledHeight = Drawable.scaleFromDensity(
489                    (int) mVectorState.mBaseHeight,sourceDensity, targetDensity, true);
490            final int left = Drawable.scaleFromDensity(
491                    opticalInsets.left, sourceDensity, targetDensity, false);
492            final int right = Drawable.scaleFromDensity(
493                    opticalInsets.right, sourceDensity, targetDensity, false);
494            final int top = Drawable.scaleFromDensity(
495                    opticalInsets.top, sourceDensity, targetDensity, false);
496            final int bottom = Drawable.scaleFromDensity(
497                    opticalInsets.bottom, sourceDensity, targetDensity, false);
498            mDpiScaledInsets = Insets.of(left, top, right, bottom);
499        } else {
500            mDpiScaledWidth = (int) mVectorState.mBaseWidth;
501            mDpiScaledHeight = (int) mVectorState.mBaseHeight;
502            mDpiScaledInsets = opticalInsets;
503        }
504
505        mDpiScaledDirty = false;
506    }
507
508    @Override
509    public boolean canApplyTheme() {
510        return (mVectorState != null && mVectorState.canApplyTheme()) || super.canApplyTheme();
511    }
512
513    @Override
514    public void applyTheme(Theme t) {
515        super.applyTheme(t);
516
517        final VectorDrawableState state = mVectorState;
518        if (state == null) {
519            return;
520        }
521
522        final boolean changedDensity = mVectorState.setDensity(
523                Drawable.resolveDensity(t.getResources(), 0));
524        mDpiScaledDirty |= changedDensity;
525
526        if (state.mThemeAttrs != null) {
527            final TypedArray a = t.resolveAttributes(
528                    state.mThemeAttrs, R.styleable.VectorDrawable);
529            try {
530                state.mCacheDirty = true;
531                updateStateFromTypedArray(a);
532            } catch (XmlPullParserException e) {
533                throw new RuntimeException(e);
534            } finally {
535                a.recycle();
536            }
537
538            // May have changed size.
539            mDpiScaledDirty = true;
540        }
541
542        // Apply theme to contained color state list.
543        if (state.mTint != null && state.mTint.canApplyTheme()) {
544            state.mTint = state.mTint.obtainForTheme(t);
545        }
546
547        if (mVectorState != null && mVectorState.canApplyTheme()) {
548            mVectorState.applyTheme(t);
549        }
550
551        // Update local properties.
552        updateLocalState(t.getResources());
553    }
554
555    /**
556     * The size of a pixel when scaled from the intrinsic dimension to the viewport dimension.
557     * This is used to calculate the path animation accuracy.
558     *
559     * @hide
560     */
561    public float getPixelSize() {
562        if (mVectorState == null ||
563                mVectorState.mBaseWidth == 0 ||
564                mVectorState.mBaseHeight == 0 ||
565                mVectorState.mViewportHeight == 0 ||
566                mVectorState.mViewportWidth == 0) {
567            return 1; // fall back to 1:1 pixel mapping.
568        }
569        float intrinsicWidth = mVectorState.mBaseWidth;
570        float intrinsicHeight = mVectorState.mBaseHeight;
571        float viewportWidth = mVectorState.mViewportWidth;
572        float viewportHeight = mVectorState.mViewportHeight;
573        float scaleX = viewportWidth / intrinsicWidth;
574        float scaleY = viewportHeight / intrinsicHeight;
575        return Math.min(scaleX, scaleY);
576    }
577
578    /** @hide */
579    public static VectorDrawable create(Resources resources, int rid) {
580        try {
581            final XmlPullParser parser = resources.getXml(rid);
582            final AttributeSet attrs = Xml.asAttributeSet(parser);
583            int type;
584            while ((type=parser.next()) != XmlPullParser.START_TAG &&
585                    type != XmlPullParser.END_DOCUMENT) {
586                // Empty loop
587            }
588            if (type != XmlPullParser.START_TAG) {
589                throw new XmlPullParserException("No start tag found");
590            }
591
592            final VectorDrawable drawable = new VectorDrawable();
593            drawable.inflate(resources, parser, attrs);
594
595            return drawable;
596        } catch (XmlPullParserException e) {
597            Log.e(LOGTAG, "parser error", e);
598        } catch (IOException e) {
599            Log.e(LOGTAG, "parser error", e);
600        }
601        return null;
602    }
603
604    @Override
605    public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser,
606            @NonNull AttributeSet attrs, @Nullable Theme theme)
607            throws XmlPullParserException, IOException {
608        if (mVectorState.mRootGroup != null || mVectorState.mNativeTree != null) {
609            // This VD has been used to display other VD resource content, clean up.
610            if (mVectorState.mRootGroup != null) {
611                // Subtract the native allocation for all the nodes.
612                VMRuntime.getRuntime().registerNativeFree(mVectorState.mRootGroup.getNativeSize());
613                // Remove child nodes' reference to tree
614                mVectorState.mRootGroup.setTree(null);
615            }
616            mVectorState.mRootGroup = new VGroup();
617            if (mVectorState.mNativeTree != null) {
618                // Subtract the native allocation for the tree wrapper, which contains root node
619                // as well as rendering related data.
620                VMRuntime.getRuntime().registerNativeFree(mVectorState.NATIVE_ALLOCATION_SIZE);
621                mVectorState.mNativeTree.release();
622            }
623            mVectorState.createNativeTree(mVectorState.mRootGroup);
624        }
625        final VectorDrawableState state = mVectorState;
626        state.setDensity(Drawable.resolveDensity(r, 0));
627
628        final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.VectorDrawable);
629        updateStateFromTypedArray(a);
630        a.recycle();
631
632        mDpiScaledDirty = true;
633
634        state.mCacheDirty = true;
635        inflateChildElements(r, parser, attrs, theme);
636
637        state.onTreeConstructionFinished();
638        // Update local properties.
639        updateLocalState(r);
640    }
641
642    private void updateStateFromTypedArray(TypedArray a) throws XmlPullParserException {
643        final VectorDrawableState state = mVectorState;
644
645        // Account for any configuration changes.
646        state.mChangingConfigurations |= a.getChangingConfigurations();
647
648        // Extract the theme attributes, if any.
649        state.mThemeAttrs = a.extractThemeAttrs();
650
651        final int tintMode = a.getInt(R.styleable.VectorDrawable_tintMode, -1);
652        if (tintMode != -1) {
653            state.mTintMode = Drawable.parseTintMode(tintMode, Mode.SRC_IN);
654        }
655
656        final ColorStateList tint = a.getColorStateList(R.styleable.VectorDrawable_tint);
657        if (tint != null) {
658            state.mTint = tint;
659        }
660
661        state.mAutoMirrored = a.getBoolean(
662                R.styleable.VectorDrawable_autoMirrored, state.mAutoMirrored);
663
664        float viewportWidth = a.getFloat(
665                R.styleable.VectorDrawable_viewportWidth, state.mViewportWidth);
666        float viewportHeight = a.getFloat(
667                R.styleable.VectorDrawable_viewportHeight, state.mViewportHeight);
668        state.setViewportSize(viewportWidth, viewportHeight);
669
670        if (state.mViewportWidth <= 0) {
671            throw new XmlPullParserException(a.getPositionDescription() +
672                    "<vector> tag requires viewportWidth > 0");
673        } else if (state.mViewportHeight <= 0) {
674            throw new XmlPullParserException(a.getPositionDescription() +
675                    "<vector> tag requires viewportHeight > 0");
676        }
677
678        state.mBaseWidth = a.getDimension(
679                R.styleable.VectorDrawable_width, state.mBaseWidth);
680        state.mBaseHeight = a.getDimension(
681                R.styleable.VectorDrawable_height, state.mBaseHeight);
682
683        if (state.mBaseWidth <= 0) {
684            throw new XmlPullParserException(a.getPositionDescription() +
685                    "<vector> tag requires width > 0");
686        } else if (state.mBaseHeight <= 0) {
687            throw new XmlPullParserException(a.getPositionDescription() +
688                    "<vector> tag requires height > 0");
689        }
690
691        final int insetLeft = a.getDimensionPixelOffset(
692                R.styleable.VectorDrawable_opticalInsetLeft, state.mOpticalInsets.left);
693        final int insetTop = a.getDimensionPixelOffset(
694                R.styleable.VectorDrawable_opticalInsetTop, state.mOpticalInsets.top);
695        final int insetRight = a.getDimensionPixelOffset(
696                R.styleable.VectorDrawable_opticalInsetRight, state.mOpticalInsets.right);
697        final int insetBottom = a.getDimensionPixelOffset(
698                R.styleable.VectorDrawable_opticalInsetBottom, state.mOpticalInsets.bottom);
699        state.mOpticalInsets = Insets.of(insetLeft, insetTop, insetRight, insetBottom);
700
701        final float alphaInFloat = a.getFloat(
702                R.styleable.VectorDrawable_alpha, state.getAlpha());
703        state.setAlpha(alphaInFloat);
704
705        final String name = a.getString(R.styleable.VectorDrawable_name);
706        if (name != null) {
707            state.mRootName = name;
708            state.mVGTargetsMap.put(name, state);
709        }
710    }
711
712    private void inflateChildElements(Resources res, XmlPullParser parser, AttributeSet attrs,
713            Theme theme) throws XmlPullParserException, IOException {
714        final VectorDrawableState state = mVectorState;
715        boolean noPathTag = true;
716
717        // Use a stack to help to build the group tree.
718        // The top of the stack is always the current group.
719        final Stack<VGroup> groupStack = new Stack<VGroup>();
720        groupStack.push(state.mRootGroup);
721
722        int eventType = parser.getEventType();
723        final int innerDepth = parser.getDepth() + 1;
724
725        // Parse everything until the end of the vector element.
726        while (eventType != XmlPullParser.END_DOCUMENT
727                && (parser.getDepth() >= innerDepth || eventType != XmlPullParser.END_TAG)) {
728            if (eventType == XmlPullParser.START_TAG) {
729                final String tagName = parser.getName();
730                final VGroup currentGroup = groupStack.peek();
731
732                if (SHAPE_PATH.equals(tagName)) {
733                    final VFullPath path = new VFullPath();
734                    path.inflate(res, attrs, theme);
735                    currentGroup.addChild(path);
736                    if (path.getPathName() != null) {
737                        state.mVGTargetsMap.put(path.getPathName(), path);
738                    }
739                    noPathTag = false;
740                    state.mChangingConfigurations |= path.mChangingConfigurations;
741                } else if (SHAPE_CLIP_PATH.equals(tagName)) {
742                    final VClipPath path = new VClipPath();
743                    path.inflate(res, attrs, theme);
744                    currentGroup.addChild(path);
745                    if (path.getPathName() != null) {
746                        state.mVGTargetsMap.put(path.getPathName(), path);
747                    }
748                    state.mChangingConfigurations |= path.mChangingConfigurations;
749                } else if (SHAPE_GROUP.equals(tagName)) {
750                    VGroup newChildGroup = new VGroup();
751                    newChildGroup.inflate(res, attrs, theme);
752                    currentGroup.addChild(newChildGroup);
753                    groupStack.push(newChildGroup);
754                    if (newChildGroup.getGroupName() != null) {
755                        state.mVGTargetsMap.put(newChildGroup.getGroupName(),
756                                newChildGroup);
757                    }
758                    state.mChangingConfigurations |= newChildGroup.mChangingConfigurations;
759                }
760            } else if (eventType == XmlPullParser.END_TAG) {
761                final String tagName = parser.getName();
762                if (SHAPE_GROUP.equals(tagName)) {
763                    groupStack.pop();
764                }
765            }
766            eventType = parser.next();
767        }
768
769        if (noPathTag) {
770            final StringBuffer tag = new StringBuffer();
771
772            if (tag.length() > 0) {
773                tag.append(" or ");
774            }
775            tag.append(SHAPE_PATH);
776
777            throw new XmlPullParserException("no " + tag + " defined");
778        }
779    }
780
781    @Override
782    public @Config int getChangingConfigurations() {
783        return super.getChangingConfigurations() | mVectorState.getChangingConfigurations();
784    }
785
786    void setAllowCaching(boolean allowCaching) {
787        nSetAllowCaching(mVectorState.getNativeRenderer(), allowCaching);
788    }
789
790    private boolean needMirroring() {
791        return isAutoMirrored() && getLayoutDirection() == LayoutDirection.RTL;
792    }
793
794    @Override
795    public void setAutoMirrored(boolean mirrored) {
796        if (mVectorState.mAutoMirrored != mirrored) {
797            mVectorState.mAutoMirrored = mirrored;
798            invalidateSelf();
799        }
800    }
801
802    @Override
803    public boolean isAutoMirrored() {
804        return mVectorState.mAutoMirrored;
805    }
806
807    /**
808     * @hide
809     */
810    public long getNativeTree() {
811        return mVectorState.getNativeRenderer();
812    }
813
814    static class VectorDrawableState extends ConstantState {
815        // Variables below need to be copied (deep copy if applicable) for mutation.
816        int[] mThemeAttrs;
817        @Config int mChangingConfigurations;
818        ColorStateList mTint = null;
819        Mode mTintMode = DEFAULT_TINT_MODE;
820        boolean mAutoMirrored;
821
822        float mBaseWidth = 0;
823        float mBaseHeight = 0;
824        float mViewportWidth = 0;
825        float mViewportHeight = 0;
826        Insets mOpticalInsets = Insets.NONE;
827        String mRootName = null;
828        VGroup mRootGroup;
829        VirtualRefBasePtr mNativeTree = null;
830
831        int mDensity = DisplayMetrics.DENSITY_DEFAULT;
832        final ArrayMap<String, Object> mVGTargetsMap = new ArrayMap<>();
833
834        // Fields for cache
835        int[] mCachedThemeAttrs;
836        ColorStateList mCachedTint;
837        Mode mCachedTintMode;
838        boolean mCachedAutoMirrored;
839        boolean mCacheDirty;
840
841        // Since sw canvas and hw canvas uses different bitmap caches, we track the allocation of
842        // these bitmaps separately.
843        int mLastSWCachePixelCount = 0;
844        int mLastHWCachePixelCount = 0;
845
846        final static Property<VectorDrawableState, Float> ALPHA =
847                new FloatProperty<VectorDrawableState>("alpha") {
848                    @Override
849                    public void setValue(VectorDrawableState state, float value) {
850                        state.setAlpha(value);
851                    }
852
853                    @Override
854                    public Float get(VectorDrawableState state) {
855                        return state.getAlpha();
856                    }
857                };
858
859        Property getProperty(String propertyName) {
860            if (ALPHA.getName().equals(propertyName)) {
861                return ALPHA;
862            }
863            return null;
864        }
865
866        // This tracks the total native allocation for all the nodes.
867        private int mAllocationOfAllNodes = 0;
868
869        private static final int NATIVE_ALLOCATION_SIZE = 316;
870
871        // If copy is not null, deep copy the given VectorDrawableState. Otherwise, create a
872        // native vector drawable tree with an empty root group.
873        public VectorDrawableState(VectorDrawableState copy) {
874            if (copy != null) {
875                mThemeAttrs = copy.mThemeAttrs;
876                mChangingConfigurations = copy.mChangingConfigurations;
877                mTint = copy.mTint;
878                mTintMode = copy.mTintMode;
879                mAutoMirrored = copy.mAutoMirrored;
880                mRootGroup = new VGroup(copy.mRootGroup, mVGTargetsMap);
881                createNativeTreeFromCopy(copy, mRootGroup);
882
883                mBaseWidth = copy.mBaseWidth;
884                mBaseHeight = copy.mBaseHeight;
885                setViewportSize(copy.mViewportWidth, copy.mViewportHeight);
886                mOpticalInsets = copy.mOpticalInsets;
887
888                mRootName = copy.mRootName;
889                mDensity = copy.mDensity;
890                if (copy.mRootName != null) {
891                    mVGTargetsMap.put(copy.mRootName, this);
892                }
893            } else {
894                mRootGroup = new VGroup();
895                createNativeTree(mRootGroup);
896            }
897            onTreeConstructionFinished();
898        }
899
900        private void createNativeTree(VGroup rootGroup) {
901            mNativeTree = new VirtualRefBasePtr(nCreateTree(rootGroup.mNativePtr));
902            // Register tree size
903            VMRuntime.getRuntime().registerNativeAllocation(NATIVE_ALLOCATION_SIZE);
904        }
905
906        // Create a new native tree with the given root group, and copy the properties from the
907        // given VectorDrawableState's native tree.
908        private void createNativeTreeFromCopy(VectorDrawableState copy, VGroup rootGroup) {
909            mNativeTree = new VirtualRefBasePtr(nCreateTreeFromCopy(
910                    copy.mNativeTree.get(), rootGroup.mNativePtr));
911            // Register tree size
912            VMRuntime.getRuntime().registerNativeAllocation(NATIVE_ALLOCATION_SIZE);
913        }
914
915        // This should be called every time after a new RootGroup and all its subtrees are created
916        // (i.e. in constructors of VectorDrawableState and in inflate).
917        void onTreeConstructionFinished() {
918            mRootGroup.setTree(mNativeTree);
919            mAllocationOfAllNodes = mRootGroup.getNativeSize();
920            VMRuntime.getRuntime().registerNativeAllocation(mAllocationOfAllNodes);
921        }
922
923        long getNativeRenderer() {
924            if (mNativeTree == null) {
925                return 0;
926            }
927            return mNativeTree.get();
928        }
929
930        public boolean canReuseCache() {
931            if (!mCacheDirty
932                    && mCachedThemeAttrs == mThemeAttrs
933                    && mCachedTint == mTint
934                    && mCachedTintMode == mTintMode
935                    && mCachedAutoMirrored == mAutoMirrored) {
936                return true;
937            }
938            updateCacheStates();
939            return false;
940        }
941
942        public void updateCacheStates() {
943            // Use shallow copy here and shallow comparison in canReuseCache(),
944            // likely hit cache miss more, but practically not much difference.
945            mCachedThemeAttrs = mThemeAttrs;
946            mCachedTint = mTint;
947            mCachedTintMode = mTintMode;
948            mCachedAutoMirrored = mAutoMirrored;
949            mCacheDirty = false;
950        }
951
952        public void applyTheme(Theme t) {
953            mRootGroup.applyTheme(t);
954        }
955
956        @Override
957        public boolean canApplyTheme() {
958            return mThemeAttrs != null
959                    || (mRootGroup != null && mRootGroup.canApplyTheme())
960                    || (mTint != null && mTint.canApplyTheme())
961                    || super.canApplyTheme();
962        }
963
964        @Override
965        public Drawable newDrawable() {
966            return new VectorDrawable(this, null);
967        }
968
969        @Override
970        public Drawable newDrawable(Resources res) {
971            return new VectorDrawable(this, res);
972        }
973
974        @Override
975        public @Config int getChangingConfigurations() {
976            return mChangingConfigurations
977                    | (mTint != null ? mTint.getChangingConfigurations() : 0);
978        }
979
980        public boolean isStateful() {
981            return (mTint != null && mTint.isStateful())
982                    || (mRootGroup != null && mRootGroup.isStateful());
983        }
984
985        public boolean hasFocusStateSpecified() {
986            return mTint != null && mTint.hasFocusStateSpecified()
987                    || (mRootGroup != null && mRootGroup.hasFocusStateSpecified());
988        }
989
990        void setViewportSize(float viewportWidth, float viewportHeight) {
991            mViewportWidth = viewportWidth;
992            mViewportHeight = viewportHeight;
993            nSetRendererViewportSize(getNativeRenderer(), viewportWidth, viewportHeight);
994        }
995
996        public final boolean setDensity(int targetDensity) {
997            if (mDensity != targetDensity) {
998                final int sourceDensity = mDensity;
999                mDensity = targetDensity;
1000                applyDensityScaling(sourceDensity, targetDensity);
1001                return true;
1002            }
1003            return false;
1004        }
1005
1006        private void applyDensityScaling(int sourceDensity, int targetDensity) {
1007            mBaseWidth = Drawable.scaleFromDensity(mBaseWidth, sourceDensity, targetDensity);
1008            mBaseHeight = Drawable.scaleFromDensity(mBaseHeight, sourceDensity, targetDensity);
1009
1010            final int insetLeft = Drawable.scaleFromDensity(
1011                    mOpticalInsets.left, sourceDensity, targetDensity, false);
1012            final int insetTop = Drawable.scaleFromDensity(
1013                    mOpticalInsets.top, sourceDensity, targetDensity, false);
1014            final int insetRight = Drawable.scaleFromDensity(
1015                    mOpticalInsets.right, sourceDensity, targetDensity, false);
1016            final int insetBottom = Drawable.scaleFromDensity(
1017                    mOpticalInsets.bottom, sourceDensity, targetDensity, false);
1018            mOpticalInsets = Insets.of(insetLeft, insetTop, insetRight, insetBottom);
1019        }
1020
1021        public boolean onStateChange(int[] stateSet) {
1022            return mRootGroup.onStateChange(stateSet);
1023        }
1024
1025        @Override
1026        public void finalize() throws Throwable {
1027            super.finalize();
1028            int bitmapCacheSize = mLastHWCachePixelCount * 4 + mLastSWCachePixelCount * 4;
1029            VMRuntime.getRuntime().registerNativeFree(NATIVE_ALLOCATION_SIZE
1030                    + mAllocationOfAllNodes + bitmapCacheSize);
1031        }
1032
1033        /**
1034         * setAlpha() and getAlpha() are used mostly for animation purpose. Return true if alpha
1035         * has changed.
1036         */
1037        public boolean setAlpha(float alpha) {
1038            return nSetRootAlpha(mNativeTree.get(), alpha);
1039        }
1040
1041        @SuppressWarnings("unused")
1042        public float getAlpha() {
1043            return nGetRootAlpha(mNativeTree.get());
1044        }
1045    }
1046
1047    static class VGroup extends VObject {
1048        private static final int ROTATION_INDEX = 0;
1049        private static final int PIVOT_X_INDEX = 1;
1050        private static final int PIVOT_Y_INDEX = 2;
1051        private static final int SCALE_X_INDEX = 3;
1052        private static final int SCALE_Y_INDEX = 4;
1053        private static final int TRANSLATE_X_INDEX = 5;
1054        private static final int TRANSLATE_Y_INDEX = 6;
1055        private static final int TRANSFORM_PROPERTY_COUNT = 7;
1056
1057        private static final int NATIVE_ALLOCATION_SIZE = 100;
1058
1059        private static final HashMap<String, Integer> sPropertyIndexMap =
1060                new HashMap<String, Integer>() {
1061                    {
1062                        put("translateX", TRANSLATE_X_INDEX);
1063                        put("translateY", TRANSLATE_Y_INDEX);
1064                        put("scaleX", SCALE_X_INDEX);
1065                        put("scaleY", SCALE_Y_INDEX);
1066                        put("pivotX", PIVOT_X_INDEX);
1067                        put("pivotY", PIVOT_Y_INDEX);
1068                        put("rotation", ROTATION_INDEX);
1069                    }
1070                };
1071
1072        static int getPropertyIndex(String propertyName) {
1073            if (sPropertyIndexMap.containsKey(propertyName)) {
1074                return sPropertyIndexMap.get(propertyName);
1075            } else {
1076                // property not found
1077                return -1;
1078            }
1079        }
1080
1081        // Below are the Properties that wrap the setters to avoid reflection overhead in animations
1082        private static final Property<VGroup, Float> TRANSLATE_X =
1083                new FloatProperty<VGroup> ("translateX") {
1084                    @Override
1085                    public void setValue(VGroup object, float value) {
1086                        object.setTranslateX(value);
1087                    }
1088
1089                    @Override
1090                    public Float get(VGroup object) {
1091                        return object.getTranslateX();
1092                    }
1093                };
1094
1095        private static final Property<VGroup, Float> TRANSLATE_Y =
1096                new FloatProperty<VGroup> ("translateY") {
1097                    @Override
1098                    public void setValue(VGroup object, float value) {
1099                        object.setTranslateY(value);
1100                    }
1101
1102                    @Override
1103                    public Float get(VGroup object) {
1104                        return object.getTranslateY();
1105                    }
1106        };
1107
1108        private static final Property<VGroup, Float> SCALE_X =
1109                new FloatProperty<VGroup> ("scaleX") {
1110                    @Override
1111                    public void setValue(VGroup object, float value) {
1112                        object.setScaleX(value);
1113                    }
1114
1115                    @Override
1116                    public Float get(VGroup object) {
1117                        return object.getScaleX();
1118                    }
1119                };
1120
1121        private static final Property<VGroup, Float> SCALE_Y =
1122                new FloatProperty<VGroup> ("scaleY") {
1123                    @Override
1124                    public void setValue(VGroup object, float value) {
1125                        object.setScaleY(value);
1126                    }
1127
1128                    @Override
1129                    public Float get(VGroup object) {
1130                        return object.getScaleY();
1131                    }
1132                };
1133
1134        private static final Property<VGroup, Float> PIVOT_X =
1135                new FloatProperty<VGroup> ("pivotX") {
1136                    @Override
1137                    public void setValue(VGroup object, float value) {
1138                        object.setPivotX(value);
1139                    }
1140
1141                    @Override
1142                    public Float get(VGroup object) {
1143                        return object.getPivotX();
1144                    }
1145                };
1146
1147        private static final Property<VGroup, Float> PIVOT_Y =
1148                new FloatProperty<VGroup> ("pivotY") {
1149                    @Override
1150                    public void setValue(VGroup object, float value) {
1151                        object.setPivotY(value);
1152                    }
1153
1154                    @Override
1155                    public Float get(VGroup object) {
1156                        return object.getPivotY();
1157                    }
1158                };
1159
1160        private static final Property<VGroup, Float> ROTATION =
1161                new FloatProperty<VGroup> ("rotation") {
1162                    @Override
1163                    public void setValue(VGroup object, float value) {
1164                        object.setRotation(value);
1165                    }
1166
1167                    @Override
1168                    public Float get(VGroup object) {
1169                        return object.getRotation();
1170                    }
1171                };
1172
1173        private static final HashMap<String, Property> sPropertyMap =
1174                new HashMap<String, Property>() {
1175                    {
1176                        put("translateX", TRANSLATE_X);
1177                        put("translateY", TRANSLATE_Y);
1178                        put("scaleX", SCALE_X);
1179                        put("scaleY", SCALE_Y);
1180                        put("pivotX", PIVOT_X);
1181                        put("pivotY", PIVOT_Y);
1182                        put("rotation", ROTATION);
1183                    }
1184                };
1185        // Temp array to store transform values obtained from native.
1186        private float[] mTransform;
1187        /////////////////////////////////////////////////////
1188        // Variables below need to be copied (deep copy if applicable) for mutation.
1189        private final ArrayList<VObject> mChildren = new ArrayList<>();
1190        private boolean mIsStateful;
1191
1192        // mLocalMatrix is updated based on the update of transformation information,
1193        // either parsed from the XML or by animation.
1194        private @Config int mChangingConfigurations;
1195        private int[] mThemeAttrs;
1196        private String mGroupName = null;
1197
1198        // The native object will be created in the constructor and will be destroyed in native
1199        // when the neither java nor native has ref to the tree. This pointer should be valid
1200        // throughout this VGroup Java object's life.
1201        private final long mNativePtr;
1202        public VGroup(VGroup copy, ArrayMap<String, Object> targetsMap) {
1203
1204            mIsStateful = copy.mIsStateful;
1205            mThemeAttrs = copy.mThemeAttrs;
1206            mGroupName = copy.mGroupName;
1207            mChangingConfigurations = copy.mChangingConfigurations;
1208            if (mGroupName != null) {
1209                targetsMap.put(mGroupName, this);
1210            }
1211            mNativePtr = nCreateGroup(copy.mNativePtr);
1212
1213            final ArrayList<VObject> children = copy.mChildren;
1214            for (int i = 0; i < children.size(); i++) {
1215                final VObject copyChild = children.get(i);
1216                if (copyChild instanceof VGroup) {
1217                    final VGroup copyGroup = (VGroup) copyChild;
1218                    addChild(new VGroup(copyGroup, targetsMap));
1219                } else {
1220                    final VPath newPath;
1221                    if (copyChild instanceof VFullPath) {
1222                        newPath = new VFullPath((VFullPath) copyChild);
1223                    } else if (copyChild instanceof VClipPath) {
1224                        newPath = new VClipPath((VClipPath) copyChild);
1225                    } else {
1226                        throw new IllegalStateException("Unknown object in the tree!");
1227                    }
1228                    addChild(newPath);
1229                    if (newPath.mPathName != null) {
1230                        targetsMap.put(newPath.mPathName, newPath);
1231                    }
1232                }
1233            }
1234        }
1235
1236        public VGroup() {
1237            mNativePtr = nCreateGroup();
1238        }
1239
1240        Property getProperty(String propertyName) {
1241            if (sPropertyMap.containsKey(propertyName)) {
1242                return sPropertyMap.get(propertyName);
1243            } else {
1244                // property not found
1245                return null;
1246            }
1247        }
1248
1249        public String getGroupName() {
1250            return mGroupName;
1251        }
1252
1253        public void addChild(VObject child) {
1254            nAddChild(mNativePtr, child.getNativePtr());
1255            mChildren.add(child);
1256            mIsStateful |= child.isStateful();
1257        }
1258
1259        @Override
1260        public void setTree(VirtualRefBasePtr treeRoot) {
1261            super.setTree(treeRoot);
1262            for (int i = 0; i < mChildren.size(); i++) {
1263                mChildren.get(i).setTree(treeRoot);
1264            }
1265        }
1266
1267        @Override
1268        public long getNativePtr() {
1269            return mNativePtr;
1270        }
1271
1272        @Override
1273        public void inflate(Resources res, AttributeSet attrs, Theme theme) {
1274            final TypedArray a = obtainAttributes(res, theme, attrs,
1275                    R.styleable.VectorDrawableGroup);
1276            updateStateFromTypedArray(a);
1277            a.recycle();
1278        }
1279
1280        void updateStateFromTypedArray(TypedArray a) {
1281            // Account for any configuration changes.
1282            mChangingConfigurations |= a.getChangingConfigurations();
1283
1284            // Extract the theme attributes, if any.
1285            mThemeAttrs = a.extractThemeAttrs();
1286            if (mTransform == null) {
1287                // Lazy initialization: If the group is created through copy constructor, this may
1288                // never get called.
1289                mTransform = new float[TRANSFORM_PROPERTY_COUNT];
1290            }
1291            boolean success = nGetGroupProperties(mNativePtr, mTransform, TRANSFORM_PROPERTY_COUNT);
1292            if (!success) {
1293                throw new RuntimeException("Error: inconsistent property count");
1294            }
1295            float rotate = a.getFloat(R.styleable.VectorDrawableGroup_rotation,
1296                    mTransform[ROTATION_INDEX]);
1297            float pivotX = a.getFloat(R.styleable.VectorDrawableGroup_pivotX,
1298                    mTransform[PIVOT_X_INDEX]);
1299            float pivotY = a.getFloat(R.styleable.VectorDrawableGroup_pivotY,
1300                    mTransform[PIVOT_Y_INDEX]);
1301            float scaleX = a.getFloat(R.styleable.VectorDrawableGroup_scaleX,
1302                    mTransform[SCALE_X_INDEX]);
1303            float scaleY = a.getFloat(R.styleable.VectorDrawableGroup_scaleY,
1304                    mTransform[SCALE_Y_INDEX]);
1305            float translateX = a.getFloat(R.styleable.VectorDrawableGroup_translateX,
1306                    mTransform[TRANSLATE_X_INDEX]);
1307            float translateY = a.getFloat(R.styleable.VectorDrawableGroup_translateY,
1308                    mTransform[TRANSLATE_Y_INDEX]);
1309
1310            final String groupName = a.getString(R.styleable.VectorDrawableGroup_name);
1311            if (groupName != null) {
1312                mGroupName = groupName;
1313                nSetName(mNativePtr, mGroupName);
1314            }
1315             nUpdateGroupProperties(mNativePtr, rotate, pivotX, pivotY, scaleX, scaleY,
1316                     translateX, translateY);
1317        }
1318
1319        @Override
1320        public boolean onStateChange(int[] stateSet) {
1321            boolean changed = false;
1322
1323            final ArrayList<VObject> children = mChildren;
1324            for (int i = 0, count = children.size(); i < count; i++) {
1325                final VObject child = children.get(i);
1326                if (child.isStateful()) {
1327                    changed |= child.onStateChange(stateSet);
1328                }
1329            }
1330
1331            return changed;
1332        }
1333
1334        @Override
1335        public boolean isStateful() {
1336            return mIsStateful;
1337        }
1338
1339        @Override
1340        public boolean hasFocusStateSpecified() {
1341            boolean result = false;
1342
1343            final ArrayList<VObject> children = mChildren;
1344            for (int i = 0, count = children.size(); i < count; i++) {
1345                final VObject child = children.get(i);
1346                if (child.isStateful()) {
1347                    result |= child.hasFocusStateSpecified();
1348                }
1349            }
1350
1351            return result;
1352        }
1353
1354        @Override
1355        int getNativeSize() {
1356            // Return the native allocation needed for the subtree.
1357            int size = NATIVE_ALLOCATION_SIZE;
1358            for (int i = 0; i < mChildren.size(); i++) {
1359                size += mChildren.get(i).getNativeSize();
1360            }
1361            return size;
1362        }
1363
1364        @Override
1365        public boolean canApplyTheme() {
1366            if (mThemeAttrs != null) {
1367                return true;
1368            }
1369
1370            final ArrayList<VObject> children = mChildren;
1371            for (int i = 0, count = children.size(); i < count; i++) {
1372                final VObject child = children.get(i);
1373                if (child.canApplyTheme()) {
1374                    return true;
1375                }
1376            }
1377
1378            return false;
1379        }
1380
1381        @Override
1382        public void applyTheme(Theme t) {
1383            if (mThemeAttrs != null) {
1384                final TypedArray a = t.resolveAttributes(mThemeAttrs,
1385                        R.styleable.VectorDrawableGroup);
1386                updateStateFromTypedArray(a);
1387                a.recycle();
1388            }
1389
1390            final ArrayList<VObject> children = mChildren;
1391            for (int i = 0, count = children.size(); i < count; i++) {
1392                final VObject child = children.get(i);
1393                if (child.canApplyTheme()) {
1394                    child.applyTheme(t);
1395
1396                    // Applying a theme may have made the child stateful.
1397                    mIsStateful |= child.isStateful();
1398                }
1399            }
1400        }
1401
1402        /* Setters and Getters, used by animator from AnimatedVectorDrawable. */
1403        @SuppressWarnings("unused")
1404        public float getRotation() {
1405            return isTreeValid() ? nGetRotation(mNativePtr) : 0;
1406        }
1407
1408        @SuppressWarnings("unused")
1409        public void setRotation(float rotation) {
1410            if (isTreeValid()) {
1411                nSetRotation(mNativePtr, rotation);
1412            }
1413        }
1414
1415        @SuppressWarnings("unused")
1416        public float getPivotX() {
1417            return isTreeValid() ? nGetPivotX(mNativePtr) : 0;
1418        }
1419
1420        @SuppressWarnings("unused")
1421        public void setPivotX(float pivotX) {
1422            if (isTreeValid()) {
1423                nSetPivotX(mNativePtr, pivotX);
1424            }
1425        }
1426
1427        @SuppressWarnings("unused")
1428        public float getPivotY() {
1429            return isTreeValid() ? nGetPivotY(mNativePtr) : 0;
1430        }
1431
1432        @SuppressWarnings("unused")
1433        public void setPivotY(float pivotY) {
1434            if (isTreeValid()) {
1435                nSetPivotY(mNativePtr, pivotY);
1436            }
1437        }
1438
1439        @SuppressWarnings("unused")
1440        public float getScaleX() {
1441            return isTreeValid() ? nGetScaleX(mNativePtr) : 0;
1442        }
1443
1444        @SuppressWarnings("unused")
1445        public void setScaleX(float scaleX) {
1446            if (isTreeValid()) {
1447                nSetScaleX(mNativePtr, scaleX);
1448            }
1449        }
1450
1451        @SuppressWarnings("unused")
1452        public float getScaleY() {
1453            return isTreeValid() ? nGetScaleY(mNativePtr) : 0;
1454        }
1455
1456        @SuppressWarnings("unused")
1457        public void setScaleY(float scaleY) {
1458            if (isTreeValid()) {
1459                nSetScaleY(mNativePtr, scaleY);
1460            }
1461        }
1462
1463        @SuppressWarnings("unused")
1464        public float getTranslateX() {
1465            return isTreeValid() ? nGetTranslateX(mNativePtr) : 0;
1466        }
1467
1468        @SuppressWarnings("unused")
1469        public void setTranslateX(float translateX) {
1470            if (isTreeValid()) {
1471                nSetTranslateX(mNativePtr, translateX);
1472            }
1473        }
1474
1475        @SuppressWarnings("unused")
1476        public float getTranslateY() {
1477            return isTreeValid() ? nGetTranslateY(mNativePtr) : 0;
1478        }
1479
1480        @SuppressWarnings("unused")
1481        public void setTranslateY(float translateY) {
1482            if (isTreeValid()) {
1483                nSetTranslateY(mNativePtr, translateY);
1484            }
1485        }
1486    }
1487
1488    /**
1489     * Common Path information for clip path and normal path.
1490     */
1491    static abstract class VPath extends VObject {
1492        protected PathParser.PathData mPathData = null;
1493
1494        String mPathName;
1495        @Config int mChangingConfigurations;
1496
1497        private static final Property<VPath, PathParser.PathData> PATH_DATA =
1498                new Property<VPath, PathParser.PathData>(PathParser.PathData.class, "pathData") {
1499                    @Override
1500                    public void set(VPath object, PathParser.PathData data) {
1501                        object.setPathData(data);
1502                    }
1503
1504                    @Override
1505                    public PathParser.PathData get(VPath object) {
1506                        return object.getPathData();
1507                    }
1508                };
1509
1510        Property getProperty(String propertyName) {
1511            if (PATH_DATA.getName().equals(propertyName)) {
1512                return PATH_DATA;
1513            }
1514            // property not found
1515            return null;
1516        }
1517
1518        public VPath() {
1519            // Empty constructor.
1520        }
1521
1522        public VPath(VPath copy) {
1523            mPathName = copy.mPathName;
1524            mChangingConfigurations = copy.mChangingConfigurations;
1525            mPathData = copy.mPathData == null ? null : new PathParser.PathData(copy.mPathData);
1526        }
1527
1528        public String getPathName() {
1529            return mPathName;
1530        }
1531
1532        /* Setters and Getters, used by animator from AnimatedVectorDrawable. */
1533        @SuppressWarnings("unused")
1534        public PathParser.PathData getPathData() {
1535            return mPathData;
1536        }
1537
1538        // TODO: Move the PathEvaluator and this setter and the getter above into native.
1539        @SuppressWarnings("unused")
1540        public void setPathData(PathParser.PathData pathData) {
1541            mPathData.setPathData(pathData);
1542            if (isTreeValid()) {
1543                nSetPathData(getNativePtr(), mPathData.getNativePtr());
1544            }
1545        }
1546    }
1547
1548    /**
1549     * Clip path, which only has name and pathData.
1550     */
1551    private static class VClipPath extends VPath {
1552        private final long mNativePtr;
1553        private static final int NATIVE_ALLOCATION_SIZE = 120;
1554
1555        public VClipPath() {
1556            mNativePtr = nCreateClipPath();
1557        }
1558
1559        public VClipPath(VClipPath copy) {
1560            super(copy);
1561            mNativePtr = nCreateClipPath(copy.mNativePtr);
1562        }
1563
1564        @Override
1565        public long getNativePtr() {
1566            return mNativePtr;
1567        }
1568
1569        @Override
1570        public void inflate(Resources r, AttributeSet attrs, Theme theme) {
1571            final TypedArray a = obtainAttributes(r, theme, attrs,
1572                    R.styleable.VectorDrawableClipPath);
1573            updateStateFromTypedArray(a);
1574            a.recycle();
1575        }
1576
1577        @Override
1578        public boolean canApplyTheme() {
1579            return false;
1580        }
1581
1582        @Override
1583        public void applyTheme(Theme theme) {
1584            // No-op.
1585        }
1586
1587        @Override
1588        public boolean onStateChange(int[] stateSet) {
1589            return false;
1590        }
1591
1592        @Override
1593        public boolean isStateful() {
1594            return false;
1595        }
1596
1597        @Override
1598        public boolean hasFocusStateSpecified() {
1599            return false;
1600        }
1601
1602        @Override
1603        int getNativeSize() {
1604            return NATIVE_ALLOCATION_SIZE;
1605        }
1606
1607        private void updateStateFromTypedArray(TypedArray a) {
1608            // Account for any configuration changes.
1609            mChangingConfigurations |= a.getChangingConfigurations();
1610
1611            final String pathName = a.getString(R.styleable.VectorDrawableClipPath_name);
1612            if (pathName != null) {
1613                mPathName = pathName;
1614                nSetName(mNativePtr, mPathName);
1615            }
1616
1617            final String pathDataString = a.getString(R.styleable.VectorDrawableClipPath_pathData);
1618            if (pathDataString != null) {
1619                mPathData = new PathParser.PathData(pathDataString);
1620                nSetPathString(mNativePtr, pathDataString, pathDataString.length());
1621            }
1622        }
1623    }
1624
1625    /**
1626     * Normal path, which contains all the fill / paint information.
1627     */
1628    static class VFullPath extends VPath {
1629        private static final int STROKE_WIDTH_INDEX = 0;
1630        private static final int STROKE_COLOR_INDEX = 1;
1631        private static final int STROKE_ALPHA_INDEX = 2;
1632        private static final int FILL_COLOR_INDEX = 3;
1633        private static final int FILL_ALPHA_INDEX = 4;
1634        private static final int TRIM_PATH_START_INDEX = 5;
1635        private static final int TRIM_PATH_END_INDEX = 6;
1636        private static final int TRIM_PATH_OFFSET_INDEX = 7;
1637        private static final int STROKE_LINE_CAP_INDEX = 8;
1638        private static final int STROKE_LINE_JOIN_INDEX = 9;
1639        private static final int STROKE_MITER_LIMIT_INDEX = 10;
1640        private static final int FILL_TYPE_INDEX = 11;
1641        private static final int TOTAL_PROPERTY_COUNT = 12;
1642
1643        private static final int NATIVE_ALLOCATION_SIZE = 264;
1644        // Property map for animatable attributes.
1645        private final static HashMap<String, Integer> sPropertyIndexMap
1646                = new HashMap<String, Integer> () {
1647            {
1648                put("strokeWidth", STROKE_WIDTH_INDEX);
1649                put("strokeColor", STROKE_COLOR_INDEX);
1650                put("strokeAlpha", STROKE_ALPHA_INDEX);
1651                put("fillColor", FILL_COLOR_INDEX);
1652                put("fillAlpha", FILL_ALPHA_INDEX);
1653                put("trimPathStart", TRIM_PATH_START_INDEX);
1654                put("trimPathEnd", TRIM_PATH_END_INDEX);
1655                put("trimPathOffset", TRIM_PATH_OFFSET_INDEX);
1656            }
1657        };
1658
1659        // Below are the Properties that wrap the setters to avoid reflection overhead in animations
1660        private static final Property<VFullPath, Float> STROKE_WIDTH =
1661                new FloatProperty<VFullPath> ("strokeWidth") {
1662                    @Override
1663                    public void setValue(VFullPath object, float value) {
1664                        object.setStrokeWidth(value);
1665                    }
1666
1667                    @Override
1668                    public Float get(VFullPath object) {
1669                        return object.getStrokeWidth();
1670                    }
1671                };
1672
1673        private static final Property<VFullPath, Integer> STROKE_COLOR =
1674                new IntProperty<VFullPath> ("strokeColor") {
1675                    @Override
1676                    public void setValue(VFullPath object, int value) {
1677                        object.setStrokeColor(value);
1678                    }
1679
1680                    @Override
1681                    public Integer get(VFullPath object) {
1682                        return object.getStrokeColor();
1683                    }
1684                };
1685
1686        private static final Property<VFullPath, Float> STROKE_ALPHA =
1687                new FloatProperty<VFullPath> ("strokeAlpha") {
1688                    @Override
1689                    public void setValue(VFullPath object, float value) {
1690                        object.setStrokeAlpha(value);
1691                    }
1692
1693                    @Override
1694                    public Float get(VFullPath object) {
1695                        return object.getStrokeAlpha();
1696                    }
1697                };
1698
1699        private static final Property<VFullPath, Integer> FILL_COLOR =
1700                new IntProperty<VFullPath>("fillColor") {
1701                    @Override
1702                    public void setValue(VFullPath object, int value) {
1703                        object.setFillColor(value);
1704                    }
1705
1706                    @Override
1707                    public Integer get(VFullPath object) {
1708                        return object.getFillColor();
1709                    }
1710                };
1711
1712        private static final Property<VFullPath, Float> FILL_ALPHA =
1713                new FloatProperty<VFullPath> ("fillAlpha") {
1714                    @Override
1715                    public void setValue(VFullPath object, float value) {
1716                        object.setFillAlpha(value);
1717                    }
1718
1719                    @Override
1720                    public Float get(VFullPath object) {
1721                        return object.getFillAlpha();
1722                    }
1723                };
1724
1725        private static final Property<VFullPath, Float> TRIM_PATH_START =
1726                new FloatProperty<VFullPath> ("trimPathStart") {
1727                    @Override
1728                    public void setValue(VFullPath object, float value) {
1729                        object.setTrimPathStart(value);
1730                    }
1731
1732                    @Override
1733                    public Float get(VFullPath object) {
1734                        return object.getTrimPathStart();
1735                    }
1736                };
1737
1738        private static final Property<VFullPath, Float> TRIM_PATH_END =
1739                new FloatProperty<VFullPath> ("trimPathEnd") {
1740                    @Override
1741                    public void setValue(VFullPath object, float value) {
1742                        object.setTrimPathEnd(value);
1743                    }
1744
1745                    @Override
1746                    public Float get(VFullPath object) {
1747                        return object.getTrimPathEnd();
1748                    }
1749                };
1750
1751        private static final Property<VFullPath, Float> TRIM_PATH_OFFSET =
1752                new FloatProperty<VFullPath> ("trimPathOffset") {
1753                    @Override
1754                    public void setValue(VFullPath object, float value) {
1755                        object.setTrimPathOffset(value);
1756                    }
1757
1758                    @Override
1759                    public Float get(VFullPath object) {
1760                        return object.getTrimPathOffset();
1761                    }
1762                };
1763
1764        private final static HashMap<String, Property> sPropertyMap
1765                = new HashMap<String, Property> () {
1766            {
1767                put("strokeWidth", STROKE_WIDTH);
1768                put("strokeColor", STROKE_COLOR);
1769                put("strokeAlpha", STROKE_ALPHA);
1770                put("fillColor", FILL_COLOR);
1771                put("fillAlpha", FILL_ALPHA);
1772                put("trimPathStart", TRIM_PATH_START);
1773                put("trimPathEnd", TRIM_PATH_END);
1774                put("trimPathOffset", TRIM_PATH_OFFSET);
1775            }
1776        };
1777
1778        // Temp array to store property data obtained from native getter.
1779        private byte[] mPropertyData;
1780        /////////////////////////////////////////////////////
1781        // Variables below need to be copied (deep copy if applicable) for mutation.
1782        private int[] mThemeAttrs;
1783
1784        ComplexColor mStrokeColors = null;
1785        ComplexColor mFillColors = null;
1786        private final long mNativePtr;
1787
1788        public VFullPath() {
1789            mNativePtr = nCreateFullPath();
1790        }
1791
1792        public VFullPath(VFullPath copy) {
1793            super(copy);
1794            mNativePtr = nCreateFullPath(copy.mNativePtr);
1795            mThemeAttrs = copy.mThemeAttrs;
1796            mStrokeColors = copy.mStrokeColors;
1797            mFillColors = copy.mFillColors;
1798        }
1799
1800        Property getProperty(String propertyName) {
1801            Property p = super.getProperty(propertyName);
1802            if (p != null) {
1803                return p;
1804            }
1805            if (sPropertyMap.containsKey(propertyName)) {
1806                return sPropertyMap.get(propertyName);
1807            } else {
1808                // property not found
1809                return null;
1810            }
1811        }
1812
1813        int getPropertyIndex(String propertyName) {
1814            if (!sPropertyIndexMap.containsKey(propertyName)) {
1815                return -1;
1816            } else {
1817                return sPropertyIndexMap.get(propertyName);
1818            }
1819        }
1820
1821        @Override
1822        public boolean onStateChange(int[] stateSet) {
1823            boolean changed = false;
1824
1825            if (mStrokeColors != null && mStrokeColors instanceof ColorStateList) {
1826                final int oldStrokeColor = getStrokeColor();
1827                final int newStrokeColor =
1828                        ((ColorStateList) mStrokeColors).getColorForState(stateSet, oldStrokeColor);
1829                changed |= oldStrokeColor != newStrokeColor;
1830                if (oldStrokeColor != newStrokeColor) {
1831                    nSetStrokeColor(mNativePtr, newStrokeColor);
1832                }
1833            }
1834
1835            if (mFillColors != null && mFillColors instanceof ColorStateList) {
1836                final int oldFillColor = getFillColor();
1837                final int newFillColor = ((ColorStateList) mFillColors).getColorForState(stateSet, oldFillColor);
1838                changed |= oldFillColor != newFillColor;
1839                if (oldFillColor != newFillColor) {
1840                    nSetFillColor(mNativePtr, newFillColor);
1841                }
1842            }
1843
1844            return changed;
1845        }
1846
1847        @Override
1848        public boolean isStateful() {
1849            return mStrokeColors != null || mFillColors != null;
1850        }
1851
1852        @Override
1853        public boolean hasFocusStateSpecified() {
1854            return (mStrokeColors != null && mStrokeColors instanceof ColorStateList &&
1855                    ((ColorStateList) mStrokeColors).hasFocusStateSpecified()) &&
1856                    (mFillColors != null && mFillColors instanceof ColorStateList &&
1857                    ((ColorStateList) mFillColors).hasFocusStateSpecified());
1858        }
1859
1860        @Override
1861        int getNativeSize() {
1862            return NATIVE_ALLOCATION_SIZE;
1863        }
1864
1865        @Override
1866        public long getNativePtr() {
1867            return mNativePtr;
1868        }
1869
1870        @Override
1871        public void inflate(Resources r, AttributeSet attrs, Theme theme) {
1872            final TypedArray a = obtainAttributes(r, theme, attrs,
1873                    R.styleable.VectorDrawablePath);
1874            updateStateFromTypedArray(a);
1875            a.recycle();
1876        }
1877
1878        private void updateStateFromTypedArray(TypedArray a) {
1879            int byteCount = TOTAL_PROPERTY_COUNT * 4;
1880            if (mPropertyData == null) {
1881                // Lazy initialization: If the path is created through copy constructor, this may
1882                // never get called.
1883                mPropertyData = new byte[byteCount];
1884            }
1885            // The bulk getters/setters of property data (e.g. stroke width, color, etc) allows us
1886            // to pull current values from native and store modifications with only two methods,
1887            // minimizing JNI overhead.
1888            boolean success = nGetFullPathProperties(mNativePtr, mPropertyData, byteCount);
1889            if (!success) {
1890                throw new RuntimeException("Error: inconsistent property count");
1891            }
1892
1893            ByteBuffer properties = ByteBuffer.wrap(mPropertyData);
1894            properties.order(ByteOrder.nativeOrder());
1895            float strokeWidth = properties.getFloat(STROKE_WIDTH_INDEX * 4);
1896            int strokeColor = properties.getInt(STROKE_COLOR_INDEX * 4);
1897            float strokeAlpha = properties.getFloat(STROKE_ALPHA_INDEX * 4);
1898            int fillColor =  properties.getInt(FILL_COLOR_INDEX * 4);
1899            float fillAlpha = properties.getFloat(FILL_ALPHA_INDEX * 4);
1900            float trimPathStart = properties.getFloat(TRIM_PATH_START_INDEX * 4);
1901            float trimPathEnd = properties.getFloat(TRIM_PATH_END_INDEX * 4);
1902            float trimPathOffset = properties.getFloat(TRIM_PATH_OFFSET_INDEX * 4);
1903            int strokeLineCap =  properties.getInt(STROKE_LINE_CAP_INDEX * 4);
1904            int strokeLineJoin = properties.getInt(STROKE_LINE_JOIN_INDEX * 4);
1905            float strokeMiterLimit = properties.getFloat(STROKE_MITER_LIMIT_INDEX * 4);
1906            int fillType = properties.getInt(FILL_TYPE_INDEX * 4);
1907            Shader fillGradient = null;
1908            Shader strokeGradient = null;
1909            // Account for any configuration changes.
1910            mChangingConfigurations |= a.getChangingConfigurations();
1911
1912            // Extract the theme attributes, if any.
1913            mThemeAttrs = a.extractThemeAttrs();
1914
1915            final String pathName = a.getString(R.styleable.VectorDrawablePath_name);
1916            if (pathName != null) {
1917                mPathName = pathName;
1918                nSetName(mNativePtr, mPathName);
1919            }
1920
1921            final String pathString = a.getString(R.styleable.VectorDrawablePath_pathData);
1922            if (pathString != null) {
1923                mPathData = new PathParser.PathData(pathString);
1924                nSetPathString(mNativePtr, pathString, pathString.length());
1925            }
1926
1927            final ComplexColor fillColors = a.getComplexColor(
1928                    R.styleable.VectorDrawablePath_fillColor);
1929            if (fillColors != null) {
1930                // If the colors is a gradient color, or the color state list is stateful, keep the
1931                // colors information. Otherwise, discard the colors and keep the default color.
1932                if (fillColors instanceof  GradientColor) {
1933                    mFillColors = fillColors;
1934                    fillGradient = ((GradientColor) fillColors).getShader();
1935                } else if (fillColors.isStateful()) {
1936                    mFillColors = fillColors;
1937                } else {
1938                    mFillColors = null;
1939                }
1940                fillColor = fillColors.getDefaultColor();
1941            }
1942
1943            final ComplexColor strokeColors = a.getComplexColor(
1944                    R.styleable.VectorDrawablePath_strokeColor);
1945            if (strokeColors != null) {
1946                // If the colors is a gradient color, or the color state list is stateful, keep the
1947                // colors information. Otherwise, discard the colors and keep the default color.
1948                if (strokeColors instanceof GradientColor) {
1949                    mStrokeColors = strokeColors;
1950                    strokeGradient = ((GradientColor) strokeColors).getShader();
1951                } else if (strokeColors.isStateful()) {
1952                    mStrokeColors = strokeColors;
1953                } else {
1954                    mStrokeColors = null;
1955                }
1956                strokeColor = strokeColors.getDefaultColor();
1957            }
1958            // Update the gradient info, even if the gradiet is null.
1959            nUpdateFullPathFillGradient(mNativePtr,
1960                    fillGradient != null ? fillGradient.getNativeInstance() : 0);
1961            nUpdateFullPathStrokeGradient(mNativePtr,
1962                    strokeGradient != null ? strokeGradient.getNativeInstance() : 0);
1963
1964            fillAlpha = a.getFloat(R.styleable.VectorDrawablePath_fillAlpha, fillAlpha);
1965
1966            strokeLineCap = a.getInt(
1967                    R.styleable.VectorDrawablePath_strokeLineCap, strokeLineCap);
1968            strokeLineJoin = a.getInt(
1969                    R.styleable.VectorDrawablePath_strokeLineJoin, strokeLineJoin);
1970            strokeMiterLimit = a.getFloat(
1971                    R.styleable.VectorDrawablePath_strokeMiterLimit, strokeMiterLimit);
1972            strokeAlpha = a.getFloat(R.styleable.VectorDrawablePath_strokeAlpha,
1973                    strokeAlpha);
1974            strokeWidth = a.getFloat(R.styleable.VectorDrawablePath_strokeWidth,
1975                    strokeWidth);
1976            trimPathEnd = a.getFloat(R.styleable.VectorDrawablePath_trimPathEnd,
1977                    trimPathEnd);
1978            trimPathOffset = a.getFloat(
1979                    R.styleable.VectorDrawablePath_trimPathOffset, trimPathOffset);
1980            trimPathStart = a.getFloat(
1981                    R.styleable.VectorDrawablePath_trimPathStart, trimPathStart);
1982            fillType = a.getInt(R.styleable.VectorDrawablePath_fillType, fillType);
1983
1984            nUpdateFullPathProperties(mNativePtr, strokeWidth, strokeColor, strokeAlpha,
1985                    fillColor, fillAlpha, trimPathStart, trimPathEnd, trimPathOffset,
1986                    strokeMiterLimit, strokeLineCap, strokeLineJoin, fillType);
1987        }
1988
1989        @Override
1990        public boolean canApplyTheme() {
1991            if (mThemeAttrs != null) {
1992                return true;
1993            }
1994
1995            boolean fillCanApplyTheme = canComplexColorApplyTheme(mFillColors);
1996            boolean strokeCanApplyTheme = canComplexColorApplyTheme(mStrokeColors);
1997            if (fillCanApplyTheme || strokeCanApplyTheme) {
1998                return true;
1999            }
2000            return false;
2001
2002        }
2003
2004        @Override
2005        public void applyTheme(Theme t) {
2006            // Resolve the theme attributes directly referred by the VectorDrawable.
2007            if (mThemeAttrs != null) {
2008                final TypedArray a = t.resolveAttributes(mThemeAttrs, R.styleable.VectorDrawablePath);
2009                updateStateFromTypedArray(a);
2010                a.recycle();
2011            }
2012
2013            // Resolve the theme attributes in-directly referred by the VectorDrawable, for example,
2014            // fillColor can refer to a color state list which itself needs to apply theme.
2015            // And this is the reason we still want to keep partial update for the path's properties.
2016            boolean fillCanApplyTheme = canComplexColorApplyTheme(mFillColors);
2017            boolean strokeCanApplyTheme = canComplexColorApplyTheme(mStrokeColors);
2018
2019            if (fillCanApplyTheme) {
2020                mFillColors = mFillColors.obtainForTheme(t);
2021                if (mFillColors instanceof GradientColor) {
2022                    nUpdateFullPathFillGradient(mNativePtr,
2023                            ((GradientColor) mFillColors).getShader().getNativeInstance());
2024                } else if (mFillColors instanceof ColorStateList) {
2025                    nSetFillColor(mNativePtr, mFillColors.getDefaultColor());
2026                }
2027            }
2028
2029            if (strokeCanApplyTheme) {
2030                mStrokeColors = mStrokeColors.obtainForTheme(t);
2031                if (mStrokeColors instanceof GradientColor) {
2032                    nUpdateFullPathStrokeGradient(mNativePtr,
2033                            ((GradientColor) mStrokeColors).getShader().getNativeInstance());
2034                } else if (mStrokeColors instanceof ColorStateList) {
2035                    nSetStrokeColor(mNativePtr, mStrokeColors.getDefaultColor());
2036                }
2037            }
2038        }
2039
2040        private boolean canComplexColorApplyTheme(ComplexColor complexColor) {
2041            return complexColor != null && complexColor.canApplyTheme();
2042        }
2043
2044        /* Setters and Getters, used by animator from AnimatedVectorDrawable. */
2045        @SuppressWarnings("unused")
2046        int getStrokeColor() {
2047            return isTreeValid() ? nGetStrokeColor(mNativePtr) : 0;
2048        }
2049
2050        @SuppressWarnings("unused")
2051        void setStrokeColor(int strokeColor) {
2052            mStrokeColors = null;
2053            if (isTreeValid()) {
2054                nSetStrokeColor(mNativePtr, strokeColor);
2055            }
2056        }
2057
2058        @SuppressWarnings("unused")
2059        float getStrokeWidth() {
2060            return isTreeValid() ? nGetStrokeWidth(mNativePtr) : 0;
2061        }
2062
2063        @SuppressWarnings("unused")
2064        void setStrokeWidth(float strokeWidth) {
2065            if (isTreeValid()) {
2066                nSetStrokeWidth(mNativePtr, strokeWidth);
2067            }
2068        }
2069
2070        @SuppressWarnings("unused")
2071        float getStrokeAlpha() {
2072            return isTreeValid() ? nGetStrokeAlpha(mNativePtr) : 0;
2073        }
2074
2075        @SuppressWarnings("unused")
2076        void setStrokeAlpha(float strokeAlpha) {
2077            if (isTreeValid()) {
2078                nSetStrokeAlpha(mNativePtr, strokeAlpha);
2079            }
2080        }
2081
2082        @SuppressWarnings("unused")
2083        int getFillColor() {
2084            return isTreeValid() ? nGetFillColor(mNativePtr) : 0;
2085        }
2086
2087        @SuppressWarnings("unused")
2088        void setFillColor(int fillColor) {
2089            mFillColors = null;
2090            if (isTreeValid()) {
2091                nSetFillColor(mNativePtr, fillColor);
2092            }
2093        }
2094
2095        @SuppressWarnings("unused")
2096        float getFillAlpha() {
2097            return isTreeValid() ? nGetFillAlpha(mNativePtr) : 0;
2098        }
2099
2100        @SuppressWarnings("unused")
2101        void setFillAlpha(float fillAlpha) {
2102            if (isTreeValid()) {
2103                nSetFillAlpha(mNativePtr, fillAlpha);
2104            }
2105        }
2106
2107        @SuppressWarnings("unused")
2108        float getTrimPathStart() {
2109            return isTreeValid() ? nGetTrimPathStart(mNativePtr) : 0;
2110        }
2111
2112        @SuppressWarnings("unused")
2113        void setTrimPathStart(float trimPathStart) {
2114            if (isTreeValid()) {
2115                nSetTrimPathStart(mNativePtr, trimPathStart);
2116            }
2117        }
2118
2119        @SuppressWarnings("unused")
2120        float getTrimPathEnd() {
2121            return isTreeValid() ? nGetTrimPathEnd(mNativePtr) : 0;
2122        }
2123
2124        @SuppressWarnings("unused")
2125        void setTrimPathEnd(float trimPathEnd) {
2126            if (isTreeValid()) {
2127                nSetTrimPathEnd(mNativePtr, trimPathEnd);
2128            }
2129        }
2130
2131        @SuppressWarnings("unused")
2132        float getTrimPathOffset() {
2133            return isTreeValid() ? nGetTrimPathOffset(mNativePtr) : 0;
2134        }
2135
2136        @SuppressWarnings("unused")
2137        void setTrimPathOffset(float trimPathOffset) {
2138            if (isTreeValid()) {
2139                nSetTrimPathOffset(mNativePtr, trimPathOffset);
2140            }
2141        }
2142    }
2143
2144    abstract static class VObject {
2145        VirtualRefBasePtr mTreePtr = null;
2146        boolean isTreeValid() {
2147            return mTreePtr != null && mTreePtr.get() != 0;
2148        }
2149        void setTree(VirtualRefBasePtr ptr) {
2150            mTreePtr = ptr;
2151        }
2152        abstract long getNativePtr();
2153        abstract void inflate(Resources r, AttributeSet attrs, Theme theme);
2154        abstract boolean canApplyTheme();
2155        abstract void applyTheme(Theme t);
2156        abstract boolean onStateChange(int[] state);
2157        abstract boolean isStateful();
2158        abstract boolean hasFocusStateSpecified();
2159        abstract int getNativeSize();
2160        abstract Property getProperty(String propertyName);
2161    }
2162
2163    private static native int nDraw(long rendererPtr, long canvasWrapperPtr,
2164            long colorFilterPtr, Rect bounds, boolean needsMirroring, boolean canReuseCache);
2165    private static native boolean nGetFullPathProperties(long pathPtr, byte[] properties,
2166            int length);
2167    private static native void nSetName(long nodePtr, String name);
2168    private static native boolean nGetGroupProperties(long groupPtr, float[] properties,
2169            int length);
2170    private static native void nSetPathString(long pathPtr, String pathString, int length);
2171
2172    // ------------- @FastNative ------------------
2173
2174    @FastNative
2175    private static native long nCreateTree(long rootGroupPtr);
2176    @FastNative
2177    private static native long nCreateTreeFromCopy(long treeToCopy, long rootGroupPtr);
2178    @FastNative
2179    private static native void nSetRendererViewportSize(long rendererPtr, float viewportWidth,
2180            float viewportHeight);
2181    @FastNative
2182    private static native boolean nSetRootAlpha(long rendererPtr, float alpha);
2183    @FastNative
2184    private static native float nGetRootAlpha(long rendererPtr);
2185    @FastNative
2186    private static native void nSetAllowCaching(long rendererPtr, boolean allowCaching);
2187
2188    @FastNative
2189    private static native long nCreateFullPath();
2190    @FastNative
2191    private static native long nCreateFullPath(long nativeFullPathPtr);
2192
2193    @FastNative
2194    private static native void nUpdateFullPathProperties(long pathPtr, float strokeWidth,
2195            int strokeColor, float strokeAlpha, int fillColor, float fillAlpha, float trimPathStart,
2196            float trimPathEnd, float trimPathOffset, float strokeMiterLimit, int strokeLineCap,
2197            int strokeLineJoin, int fillType);
2198    @FastNative
2199    private static native void nUpdateFullPathFillGradient(long pathPtr, long fillGradientPtr);
2200    @FastNative
2201    private static native void nUpdateFullPathStrokeGradient(long pathPtr, long strokeGradientPtr);
2202
2203    @FastNative
2204    private static native long nCreateClipPath();
2205    @FastNative
2206    private static native long nCreateClipPath(long clipPathPtr);
2207
2208    @FastNative
2209    private static native long nCreateGroup();
2210    @FastNative
2211    private static native long nCreateGroup(long groupPtr);
2212    @FastNative
2213    private static native void nUpdateGroupProperties(long groupPtr, float rotate, float pivotX,
2214            float pivotY, float scaleX, float scaleY, float translateX, float translateY);
2215
2216    @FastNative
2217    private static native void nAddChild(long groupPtr, long nodePtr);
2218
2219    /**
2220     * The setters and getters below for paths and groups are here temporarily, and will be
2221     * removed once the animation in AVD is replaced with RenderNodeAnimator, in which case the
2222     * animation will modify these properties in native. By then no JNI hopping would be necessary
2223     * for VD during animation, and these setters and getters will be obsolete.
2224     */
2225    // Setters and getters during animation.
2226    @FastNative
2227    private static native float nGetRotation(long groupPtr);
2228    @FastNative
2229    private static native void nSetRotation(long groupPtr, float rotation);
2230    @FastNative
2231    private static native float nGetPivotX(long groupPtr);
2232    @FastNative
2233    private static native void nSetPivotX(long groupPtr, float pivotX);
2234    @FastNative
2235    private static native float nGetPivotY(long groupPtr);
2236    @FastNative
2237    private static native void nSetPivotY(long groupPtr, float pivotY);
2238    @FastNative
2239    private static native float nGetScaleX(long groupPtr);
2240    @FastNative
2241    private static native void nSetScaleX(long groupPtr, float scaleX);
2242    @FastNative
2243    private static native float nGetScaleY(long groupPtr);
2244    @FastNative
2245    private static native void nSetScaleY(long groupPtr, float scaleY);
2246    @FastNative
2247    private static native float nGetTranslateX(long groupPtr);
2248    @FastNative
2249    private static native void nSetTranslateX(long groupPtr, float translateX);
2250    @FastNative
2251    private static native float nGetTranslateY(long groupPtr);
2252    @FastNative
2253    private static native void nSetTranslateY(long groupPtr, float translateY);
2254
2255    // Setters and getters for VPath during animation.
2256    @FastNative
2257    private static native void nSetPathData(long pathPtr, long pathDataPtr);
2258    @FastNative
2259    private static native float nGetStrokeWidth(long pathPtr);
2260    @FastNative
2261    private static native void nSetStrokeWidth(long pathPtr, float width);
2262    @FastNative
2263    private static native int nGetStrokeColor(long pathPtr);
2264    @FastNative
2265    private static native void nSetStrokeColor(long pathPtr, int strokeColor);
2266    @FastNative
2267    private static native float nGetStrokeAlpha(long pathPtr);
2268    @FastNative
2269    private static native void nSetStrokeAlpha(long pathPtr, float alpha);
2270    @FastNative
2271    private static native int nGetFillColor(long pathPtr);
2272    @FastNative
2273    private static native void nSetFillColor(long pathPtr, int fillColor);
2274    @FastNative
2275    private static native float nGetFillAlpha(long pathPtr);
2276    @FastNative
2277    private static native void nSetFillAlpha(long pathPtr, float fillAlpha);
2278    @FastNative
2279    private static native float nGetTrimPathStart(long pathPtr);
2280    @FastNative
2281    private static native void nSetTrimPathStart(long pathPtr, float trimPathStart);
2282    @FastNative
2283    private static native float nGetTrimPathEnd(long pathPtr);
2284    @FastNative
2285    private static native void nSetTrimPathEnd(long pathPtr, float trimPathEnd);
2286    @FastNative
2287    private static native float nGetTrimPathOffset(long pathPtr);
2288    @FastNative
2289    private static native void nSetTrimPathOffset(long pathPtr, float trimPathOffset);
2290}
2291