Home | History | Annotate | Download | only in drawable
      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";
    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 
    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      */
    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      */
    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
    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      */
    387     public void clearMutated() {
    388         super.clearMutated();
    389         mMutated = false;
    390     }
    391 
    392     Object getTargetByName(String name) {
    393         return mVectorState.mVGTargetsMap.get(name);
    394     }
    395 
    396     @Override
    397     public ConstantState getConstantState() {
    398         mVectorState.mChangingConfigurations = getChangingConfigurations();
    399         return mVectorState;
    400     }
    401 
    402     @Override
    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
    448     public int getAlpha() {
    449         return (int) (mVectorState.getAlpha() * 255);
    450     }
    451 
    452     @Override
    453     public void setAlpha(int alpha) {
    454         if (mVectorState.setAlpha(alpha / 255f)) {
    455             invalidateSelf();
    456         }
    457     }
    458 
    459     @Override
    460     public void setColorFilter(ColorFilter colorFilter) {
    461         mColorFilter = colorFilter;
    462         invalidateSelf();
    463     }
    464 
    465     @Override
    466     public ColorFilter getColorFilter() {
    467         return mColorFilter;
    468     }
    469 
    470     @Override
    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
    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
    491     public boolean isStateful() {
    492         return super.isStateful() || (mVectorState != null && mVectorState.isStateful());
    493     }
    494 
    495     /** @hide */
    496     @Override
    497     public boolean hasFocusStateSpecified() {
    498         return mVectorState != null && mVectorState.hasFocusStateSpecified();
    499     }
    500 
    501     @Override
    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
    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
    531     public int getIntrinsicWidth() {
    532         if (mDpiScaledDirty) {
    533             computeVectorSize();
    534         }
    535         return mDpiScaledWidth;
    536     }
    537 
    538     @Override
    539     public int getIntrinsicHeight() {
    540         if (mDpiScaledDirty) {
    541             computeVectorSize();
    542         }
    543         return mDpiScaledHeight;
    544     }
    545 
    546     /** @hide */
    547     @Override
    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      */
    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
    588     public boolean canApplyTheme() {
    589         return (mVectorState != null && mVectorState.canApplyTheme()) || super.canApplyTheme();
    590     }
    591 
    592     @Override
    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      */
    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 */
    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
    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 
    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 
    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
    867     public @Config int getChangingConfigurations() {
    868         return super.getChangingConfigurations() | mVectorState.getChangingConfigurations();
    869     }
    870 
    871     void setAllowCaching(boolean allowCaching) {
    872         nSetAllowCaching(mVectorState.getNativeRenderer(), allowCaching);
    873     }
    874 
    875     private boolean needMirroring() {
    876         return isAutoMirrored() && getLayoutDirection() == LayoutDirection.RTL;
    877     }
    878 
    879     @Override
    880     public void setAutoMirrored(boolean mirrored) {
    881         if (mVectorState.mAutoMirrored != mirrored) {
    882             mVectorState.mAutoMirrored = mirrored;
    883             invalidateSelf();
    884         }
    885     }
    886 
    887     @Override
    888     public boolean isAutoMirrored() {
    889         return mVectorState.mAutoMirrored;
    890     }
    891 
    892     /**
    893      * @hide
    894      */
    895     public long getNativeTree() {
    896         return mVectorState.getNativeRenderer();
    897     }
    898 
    899     /**
    900      * @hide
    901      */
    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 
    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.
    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 
    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.
   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).
   1009         void onTreeConstructionFinished() {
   1010             mRootGroup.setTree(mNativeTree);
   1011             mAllocationOfAllNodes = mRootGroup.getNativeSize();
   1012             VMRuntime.getRuntime().registerNativeAllocation(mAllocationOfAllNodes);
   1013         }
   1014 
   1015         long getNativeRenderer() {
   1016             if (mNativeTree == null) {
   1017                 return 0;
   1018             }
   1019             return mNativeTree.get();
   1020         }
   1021 
   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 
   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 
   1044         public void applyTheme(Theme t) {
   1045             mRootGroup.applyTheme(t);
   1046         }
   1047 
   1048         @Override
   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
   1057         public Drawable newDrawable() {
   1058             return new VectorDrawable(this, null);
   1059         }
   1060 
   1061         @Override
   1062         public Drawable newDrawable(Resources res) {
   1063             return new VectorDrawable(this, res);
   1064         }
   1065 
   1066         @Override
   1067         public @Config int getChangingConfigurations() {
   1068             return mChangingConfigurations
   1069                     | (mTint != null ? mTint.getChangingConfigurations() : 0);
   1070         }
   1071 
   1072         public boolean isStateful() {
   1073             return (mTint != null && mTint.isStateful())
   1074                     || (mRootGroup != null && mRootGroup.isStateful());
   1075         }
   1076 
   1077         public boolean hasFocusStateSpecified() {
   1078             return mTint != null && mTint.hasFocusStateSpecified()
   1079                     || (mRootGroup != null && mRootGroup.hasFocusStateSpecified());
   1080         }
   1081 
   1082         void setViewportSize(float viewportWidth, float viewportHeight) {
   1083             mViewportWidth = viewportWidth;
   1084             mViewportHeight = viewportHeight;
   1085             nSetRendererViewportSize(getNativeRenderer(), viewportWidth, viewportHeight);
   1086         }
   1087 
   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 
   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 
   1114         public boolean onStateChange(int[] stateSet) {
   1115             return mRootGroup.onStateChange(stateSet);
   1116         }
   1117 
   1118         @Override
   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          */
   1130         public boolean setAlpha(float alpha) {
   1131             return nSetRootAlpha(mNativeTree.get(), alpha);
   1132         }
   1133 
   1134         @SuppressWarnings("unused")
   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 
   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;
   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 
   1329         public VGroup() {
   1330             mNativePtr = nCreateGroup();
   1331         }
   1332 
   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 
   1342         public String getGroupName() {
   1343             return mGroupName;
   1344         }
   1345 
   1346         public void addChild(VObject child) {
   1347             nAddChild(mNativePtr, child.getNativePtr());
   1348             mChildren.add(child);
   1349             mIsStateful |= child.isStateful();
   1350         }
   1351 
   1352         @Override
   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
   1361         public long getNativePtr() {
   1362             return mNativePtr;
   1363         }
   1364 
   1365         @Override
   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 
   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
   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
   1428         public boolean isStateful() {
   1429             return mIsStateful;
   1430         }
   1431 
   1432         @Override
   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
   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
   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
   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")
   1497         public float getRotation() {
   1498             return isTreeValid() ? nGetRotation(mNativePtr) : 0;
   1499         }
   1500 
   1501         @SuppressWarnings("unused")
   1502         public void setRotation(float rotation) {
   1503             if (isTreeValid()) {
   1504                 nSetRotation(mNativePtr, rotation);
   1505             }
   1506         }
   1507 
   1508         @SuppressWarnings("unused")
   1509         public float getPivotX() {
   1510             return isTreeValid() ? nGetPivotX(mNativePtr) : 0;
   1511         }
   1512 
   1513         @SuppressWarnings("unused")
   1514         public void setPivotX(float pivotX) {
   1515             if (isTreeValid()) {
   1516                 nSetPivotX(mNativePtr, pivotX);
   1517             }
   1518         }
   1519 
   1520         @SuppressWarnings("unused")
   1521         public float getPivotY() {
   1522             return isTreeValid() ? nGetPivotY(mNativePtr) : 0;
   1523         }
   1524 
   1525         @SuppressWarnings("unused")
   1526         public void setPivotY(float pivotY) {
   1527             if (isTreeValid()) {
   1528                 nSetPivotY(mNativePtr, pivotY);
   1529             }
   1530         }
   1531 
   1532         @SuppressWarnings("unused")
   1533         public float getScaleX() {
   1534             return isTreeValid() ? nGetScaleX(mNativePtr) : 0;
   1535         }
   1536 
   1537         @SuppressWarnings("unused")
   1538         public void setScaleX(float scaleX) {
   1539             if (isTreeValid()) {
   1540                 nSetScaleX(mNativePtr, scaleX);
   1541             }
   1542         }
   1543 
   1544         @SuppressWarnings("unused")
   1545         public float getScaleY() {
   1546             return isTreeValid() ? nGetScaleY(mNativePtr) : 0;
   1547         }
   1548 
   1549         @SuppressWarnings("unused")
   1550         public void setScaleY(float scaleY) {
   1551             if (isTreeValid()) {
   1552                 nSetScaleY(mNativePtr, scaleY);
   1553             }
   1554         }
   1555 
   1556         @SuppressWarnings("unused")
   1557         public float getTranslateX() {
   1558             return isTreeValid() ? nGetTranslateX(mNativePtr) : 0;
   1559         }
   1560 
   1561         @SuppressWarnings("unused")
   1562         public void setTranslateX(float translateX) {
   1563             if (isTreeValid()) {
   1564                 nSetTranslateX(mNativePtr, translateX);
   1565             }
   1566         }
   1567 
   1568         @SuppressWarnings("unused")
   1569         public float getTranslateY() {
   1570             return isTreeValid() ? nGetTranslateY(mNativePtr) : 0;
   1571         }
   1572 
   1573         @SuppressWarnings("unused")
   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 
   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 
   1611         public VPath() {
   1612             // Empty constructor.
   1613         }
   1614 
   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 
   1621         public String getPathName() {
   1622             return mPathName;
   1623         }
   1624 
   1625         /* Setters and Getters, used by animator from AnimatedVectorDrawable. */
   1626         @SuppressWarnings("unused")
   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")
   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 
   1648         public VClipPath() {
   1649             mNativePtr = nCreateClipPath();
   1650         }
   1651 
   1652         public VClipPath(VClipPath copy) {
   1653             super(copy);
   1654             mNativePtr = nCreateClipPath(copy.mNativePtr);
   1655         }
   1656 
   1657         @Override
   1658         public long getNativePtr() {
   1659             return mNativePtr;
   1660         }
   1661 
   1662         @Override
   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
   1671         public boolean canApplyTheme() {
   1672             return false;
   1673         }
   1674 
   1675         @Override
   1676         public void applyTheme(Theme theme) {
   1677             // No-op.
   1678         }
   1679 
   1680         @Override
   1681         public boolean onStateChange(int[] stateSet) {
   1682             return false;
   1683         }
   1684 
   1685         @Override
   1686         public boolean isStateful() {
   1687             return false;
   1688         }
   1689 
   1690         @Override
   1691         public boolean hasFocusStateSpecified() {
   1692             return false;
   1693         }
   1694 
   1695         @Override
   1696         int getNativeSize() {
   1697             return NATIVE_ALLOCATION_SIZE;
   1698         }
   1699 
   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 
   1881         public VFullPath() {
   1882             mNativePtr = nCreateFullPath();
   1883         }
   1884 
   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 
   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 
   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
   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
   1941         public boolean isStateful() {
   1942             return mStrokeColors != null || mFillColors != null;
   1943         }
   1944 
   1945         @Override
   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
   1954         int getNativeSize() {
   1955             return NATIVE_ALLOCATION_SIZE;
   1956         }
   1957 
   1958         @Override
   1959         public long getNativePtr() {
   1960             return mNativePtr;
   1961         }
   1962 
   1963         @Override
   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 
   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
   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
   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 
   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")
   2139         int getStrokeColor() {
   2140             return isTreeValid() ? nGetStrokeColor(mNativePtr) : 0;
   2141         }
   2142 
   2143         @SuppressWarnings("unused")
   2144         void setStrokeColor(int strokeColor) {
   2145             mStrokeColors = null;
   2146             if (isTreeValid()) {
   2147                 nSetStrokeColor(mNativePtr, strokeColor);
   2148             }
   2149         }
   2150 
   2151         @SuppressWarnings("unused")
   2152         float getStrokeWidth() {
   2153             return isTreeValid() ? nGetStrokeWidth(mNativePtr) : 0;
   2154         }
   2155 
   2156         @SuppressWarnings("unused")
   2157         void setStrokeWidth(float strokeWidth) {
   2158             if (isTreeValid()) {
   2159                 nSetStrokeWidth(mNativePtr, strokeWidth);
   2160             }
   2161         }
   2162 
   2163         @SuppressWarnings("unused")
   2164         float getStrokeAlpha() {
   2165             return isTreeValid() ? nGetStrokeAlpha(mNativePtr) : 0;
   2166         }
   2167 
   2168         @SuppressWarnings("unused")
   2169         void setStrokeAlpha(float strokeAlpha) {
   2170             if (isTreeValid()) {
   2171                 nSetStrokeAlpha(mNativePtr, strokeAlpha);
   2172             }
   2173         }
   2174 
   2175         @SuppressWarnings("unused")
   2176         int getFillColor() {
   2177             return isTreeValid() ? nGetFillColor(mNativePtr) : 0;
   2178         }
   2179 
   2180         @SuppressWarnings("unused")
   2181         void setFillColor(int fillColor) {
   2182             mFillColors = null;
   2183             if (isTreeValid()) {
   2184                 nSetFillColor(mNativePtr, fillColor);
   2185             }
   2186         }
   2187 
   2188         @SuppressWarnings("unused")
   2189         float getFillAlpha() {
   2190             return isTreeValid() ? nGetFillAlpha(mNativePtr) : 0;
   2191         }
   2192 
   2193         @SuppressWarnings("unused")
   2194         void setFillAlpha(float fillAlpha) {
   2195             if (isTreeValid()) {
   2196                 nSetFillAlpha(mNativePtr, fillAlpha);
   2197             }
   2198         }
   2199 
   2200         @SuppressWarnings("unused")
   2201         float getTrimPathStart() {
   2202             return isTreeValid() ? nGetTrimPathStart(mNativePtr) : 0;
   2203         }
   2204 
   2205         @SuppressWarnings("unused")
   2206         void setTrimPathStart(float trimPathStart) {
   2207             if (isTreeValid()) {
   2208                 nSetTrimPathStart(mNativePtr, trimPathStart);
   2209             }
   2210         }
   2211 
   2212         @SuppressWarnings("unused")
   2213         float getTrimPathEnd() {
   2214             return isTreeValid() ? nGetTrimPathEnd(mNativePtr) : 0;
   2215         }
   2216 
   2217         @SuppressWarnings("unused")
   2218         void setTrimPathEnd(float trimPathEnd) {
   2219             if (isTreeValid()) {
   2220                 nSetTrimPathEnd(mNativePtr, trimPathEnd);
   2221             }
   2222         }
   2223 
   2224         @SuppressWarnings("unused")
   2225         float getTrimPathOffset() {
   2226             return isTreeValid() ? nGetTrimPathOffset(mNativePtr) : 0;
   2227         }
   2228 
   2229         @SuppressWarnings("unused")
   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;
   2239         boolean isTreeValid() {
   2240             return mTreePtr != null && mTreePtr.get() != 0;
   2241         }
   2242         void setTree(VirtualRefBasePtr ptr) {
   2243             mTreePtr = ptr;
   2244         }
   2245         abstract long getNativePtr();
   2246         abstract void inflate(Resources r, AttributeSet attrs, Theme theme);
   2247         abstract boolean canApplyTheme();
   2248         abstract void applyTheme(Theme t);
   2249         abstract boolean onStateChange(int[] state);
   2250         abstract boolean isStateful();
   2251         abstract boolean hasFocusStateSpecified();
   2252         abstract int getNativeSize();
   2253         abstract Property getProperty(String propertyName);
   2254     }
   2255 
   2256     private static native int nDraw(long rendererPtr, long canvasWrapperPtr,
   2257             long colorFilterPtr, Rect bounds, boolean needsMirroring, boolean canReuseCache);
   2258     private static native boolean nGetFullPathProperties(long pathPtr, byte[] properties,
   2259             int length);
   2260     private static native void nSetName(long nodePtr, String name);
   2261     private static native boolean nGetGroupProperties(long groupPtr, float[] properties,
   2262             int length);
   2263     private static native void nSetPathString(long pathPtr, String pathString, int length);
   2264 
   2265     // ------------- @FastNative ------------------
   2266 
   2267     @FastNative
   2268     private static native long nCreateTree(long rootGroupPtr);
   2269     @FastNative
   2270     private static native long nCreateTreeFromCopy(long treeToCopy, long rootGroupPtr);
   2271     @FastNative
   2272     private static native void nSetRendererViewportSize(long rendererPtr, float viewportWidth,
   2273             float viewportHeight);
   2274     @FastNative
   2275     private static native boolean nSetRootAlpha(long rendererPtr, float alpha);
   2276     @FastNative
   2277     private static native float nGetRootAlpha(long rendererPtr);
   2278     @FastNative
   2279     private static native void nSetAntiAlias(long rendererPtr, boolean aa);
   2280     @FastNative
   2281     private static native void nSetAllowCaching(long rendererPtr, boolean allowCaching);
   2282 
   2283     @FastNative
   2284     private static native long nCreateFullPath();
   2285     @FastNative
   2286     private static native long nCreateFullPath(long nativeFullPathPtr);
   2287 
   2288     @FastNative
   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
   2294     private static native void nUpdateFullPathFillGradient(long pathPtr, long fillGradientPtr);
   2295     @FastNative
   2296     private static native void nUpdateFullPathStrokeGradient(long pathPtr, long strokeGradientPtr);
   2297 
   2298     @FastNative
   2299     private static native long nCreateClipPath();
   2300     @FastNative
   2301     private static native long nCreateClipPath(long clipPathPtr);
   2302 
   2303     @FastNative
   2304     private static native long nCreateGroup();
   2305     @FastNative
   2306     private static native long nCreateGroup(long groupPtr);
   2307     @FastNative
   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
   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
   2322     private static native float nGetRotation(long groupPtr);
   2323     @FastNative
   2324     private static native void nSetRotation(long groupPtr, float rotation);
   2325     @FastNative
   2326     private static native float nGetPivotX(long groupPtr);
   2327     @FastNative
   2328     private static native void nSetPivotX(long groupPtr, float pivotX);
   2329     @FastNative
   2330     private static native float nGetPivotY(long groupPtr);
   2331     @FastNative
   2332     private static native void nSetPivotY(long groupPtr, float pivotY);
   2333     @FastNative
   2334     private static native float nGetScaleX(long groupPtr);
   2335     @FastNative
   2336     private static native void nSetScaleX(long groupPtr, float scaleX);
   2337     @FastNative
   2338     private static native float nGetScaleY(long groupPtr);
   2339     @FastNative
   2340     private static native void nSetScaleY(long groupPtr, float scaleY);
   2341     @FastNative
   2342     private static native float nGetTranslateX(long groupPtr);
   2343     @FastNative
   2344     private static native void nSetTranslateX(long groupPtr, float translateX);
   2345     @FastNative
   2346     private static native float nGetTranslateY(long groupPtr);
   2347     @FastNative
   2348     private static native void nSetTranslateY(long groupPtr, float translateY);
   2349 
   2350     // Setters and getters for VPath during animation.
   2351     @FastNative
   2352     private static native void nSetPathData(long pathPtr, long pathDataPtr);
   2353     @FastNative
   2354     private static native float nGetStrokeWidth(long pathPtr);
   2355     @FastNative
   2356     private static native void nSetStrokeWidth(long pathPtr, float width);
   2357     @FastNative
   2358     private static native int nGetStrokeColor(long pathPtr);
   2359     @FastNative
   2360     private static native void nSetStrokeColor(long pathPtr, int strokeColor);
   2361     @FastNative
   2362     private static native float nGetStrokeAlpha(long pathPtr);
   2363     @FastNative
   2364     private static native void nSetStrokeAlpha(long pathPtr, float alpha);
   2365     @FastNative
   2366     private static native int nGetFillColor(long pathPtr);
   2367     @FastNative
   2368     private static native void nSetFillColor(long pathPtr, int fillColor);
   2369     @FastNative
   2370     private static native float nGetFillAlpha(long pathPtr);
   2371     @FastNative
   2372     private static native void nSetFillAlpha(long pathPtr, float fillAlpha);
   2373     @FastNative
   2374     private static native float nGetTrimPathStart(long pathPtr);
   2375     @FastNative
   2376     private static native void nSetTrimPathStart(long pathPtr, float trimPathStart);
   2377     @FastNative
   2378     private static native float nGetTrimPathEnd(long pathPtr);
   2379     @FastNative
   2380     private static native void nSetTrimPathEnd(long pathPtr, float trimPathEnd);
   2381     @FastNative
   2382     private static native float nGetTrimPathOffset(long pathPtr);
   2383     @FastNative
   2384     private static native void nSetTrimPathOffset(long pathPtr, float trimPathOffset);
   2385 }
   2386