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