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