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