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