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