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