1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.graphics.drawable; 18 19 import com.android.layoutlib.bridge.impl.DelegateManager; 20 import com.android.tools.layoutlib.annotations.LayoutlibDelegate; 21 22 import android.annotation.NonNull; 23 import android.content.res.Resources; 24 import android.content.res.Resources.Theme; 25 import android.content.res.TypedArray; 26 import android.graphics.BaseCanvas_Delegate; 27 import android.graphics.Canvas_Delegate; 28 import android.graphics.Color; 29 import android.graphics.Matrix; 30 import android.graphics.Paint; 31 import android.graphics.Paint.Cap; 32 import android.graphics.Paint.Join; 33 import android.graphics.Paint_Delegate; 34 import android.graphics.Path; 35 import android.graphics.PathMeasure; 36 import android.graphics.Path_Delegate; 37 import android.graphics.Rect; 38 import android.graphics.Region; 39 import android.graphics.Region.Op; 40 import android.graphics.Shader_Delegate; 41 import android.util.ArrayMap; 42 import android.util.AttributeSet; 43 import android.util.Log; 44 import android.util.MathUtils; 45 import android.util.PathParser_Delegate; 46 47 import java.nio.ByteBuffer; 48 import java.nio.ByteOrder; 49 import java.nio.FloatBuffer; 50 import java.util.ArrayList; 51 import java.util.function.Consumer; 52 53 import static android.graphics.Canvas.CLIP_SAVE_FLAG; 54 import static android.graphics.Canvas.MATRIX_SAVE_FLAG; 55 import static android.graphics.Paint.Cap.BUTT; 56 import static android.graphics.Paint.Cap.ROUND; 57 import static android.graphics.Paint.Cap.SQUARE; 58 import static android.graphics.Paint.Join.BEVEL; 59 import static android.graphics.Paint.Join.MITER; 60 import static android.graphics.Paint.Style; 61 62 /** 63 * Delegate used to provide new implementation of a select few methods of {@link VectorDrawable} 64 * <p> 65 * Through the layoutlib_create tool, the original methods of VectorDrawable have been replaced by 66 * calls to methods of the same name in this delegate class. 67 */ 68 @SuppressWarnings("unused") 69 public class VectorDrawable_Delegate { 70 private static final String LOGTAG = VectorDrawable_Delegate.class.getSimpleName(); 71 private static final boolean DBG_VECTOR_DRAWABLE = false; 72 73 private static final DelegateManager<VNativeObject> sPathManager = 74 new DelegateManager<>(VNativeObject.class); 75 76 private static long addNativeObject(VNativeObject object) { 77 long ptr = sPathManager.addNewDelegate(object); 78 object.setNativePtr(ptr); 79 80 return ptr; 81 } 82 83 /** 84 * Obtains styled attributes from the theme, if available, or unstyled resources if the theme is 85 * null. 86 */ 87 private static TypedArray obtainAttributes( 88 Resources res, Theme theme, AttributeSet set, int[] attrs) { 89 if (theme == null) { 90 return res.obtainAttributes(set, attrs); 91 } 92 return theme.obtainStyledAttributes(set, attrs, 0, 0); 93 } 94 95 private static int applyAlpha(int color, float alpha) { 96 int alphaBytes = Color.alpha(color); 97 color &= 0x00FFFFFF; 98 color |= ((int) (alphaBytes * alpha)) << 24; 99 return color; 100 } 101 102 @LayoutlibDelegate 103 static long nCreateTree(long rootGroupPtr) { 104 return addNativeObject(new VPathRenderer_Delegate(rootGroupPtr)); 105 } 106 107 @LayoutlibDelegate 108 static long nCreateTreeFromCopy(long rendererToCopyPtr, long rootGroupPtr) { 109 VPathRenderer_Delegate rendererToCopy = VNativeObject.getDelegate(rendererToCopyPtr); 110 return addNativeObject(new VPathRenderer_Delegate(rendererToCopy, 111 rootGroupPtr)); 112 } 113 114 @LayoutlibDelegate 115 static void nSetRendererViewportSize(long rendererPtr, float viewportWidth, 116 float viewportHeight) { 117 VPathRenderer_Delegate nativePathRenderer = VNativeObject.getDelegate(rendererPtr); 118 nativePathRenderer.mViewportWidth = viewportWidth; 119 nativePathRenderer.mViewportHeight = viewportHeight; 120 } 121 122 @LayoutlibDelegate 123 static boolean nSetRootAlpha(long rendererPtr, float alpha) { 124 VPathRenderer_Delegate nativePathRenderer = VNativeObject.getDelegate(rendererPtr); 125 nativePathRenderer.setRootAlpha(alpha); 126 127 return true; 128 } 129 130 @LayoutlibDelegate 131 static float nGetRootAlpha(long rendererPtr) { 132 VPathRenderer_Delegate nativePathRenderer = VNativeObject.getDelegate(rendererPtr); 133 134 return nativePathRenderer.getRootAlpha(); 135 } 136 137 @LayoutlibDelegate 138 static void nSetAntiAlias(long rendererPtr, boolean aa) { 139 VPathRenderer_Delegate nativePathRenderer = VNativeObject.getDelegate(rendererPtr); 140 nativePathRenderer.setAntiAlias(aa); 141 } 142 143 @LayoutlibDelegate 144 static void nSetAllowCaching(long rendererPtr, boolean allowCaching) { 145 // ignored 146 } 147 148 @LayoutlibDelegate 149 static int nDraw(long rendererPtr, long canvasWrapperPtr, 150 long colorFilterPtr, Rect bounds, boolean needsMirroring, boolean canReuseCache) { 151 VPathRenderer_Delegate nativePathRenderer = VNativeObject.getDelegate(rendererPtr); 152 153 Canvas_Delegate.nSave(canvasWrapperPtr, MATRIX_SAVE_FLAG | CLIP_SAVE_FLAG); 154 Canvas_Delegate.nClipRect(canvasWrapperPtr, 155 bounds.left, bounds.top, bounds.right, bounds.bottom, 156 Region.Op.INTERSECT.nativeInt); 157 Canvas_Delegate.nTranslate(canvasWrapperPtr, bounds.left, bounds.top); 158 159 if (needsMirroring) { 160 Canvas_Delegate.nTranslate(canvasWrapperPtr, bounds.width(), 0); 161 Canvas_Delegate.nScale(canvasWrapperPtr, -1.0f, 1.0f); 162 } 163 164 // At this point, canvas has been translated to the right position. 165 // And we use this bound for the destination rect for the drawBitmap, so 166 // we offset to (0, 0); 167 bounds.offsetTo(0, 0); 168 nativePathRenderer.draw(canvasWrapperPtr, colorFilterPtr, bounds.width(), bounds.height()); 169 170 Canvas_Delegate.nRestore(canvasWrapperPtr); 171 172 return bounds.width() * bounds.height(); 173 } 174 175 @LayoutlibDelegate 176 static long nCreateFullPath() { 177 return addNativeObject(new VFullPath_Delegate()); 178 } 179 180 @LayoutlibDelegate 181 static long nCreateFullPath(long nativeFullPathPtr) { 182 VFullPath_Delegate original = VNativeObject.getDelegate(nativeFullPathPtr); 183 return addNativeObject(new VFullPath_Delegate(original)); 184 } 185 186 @LayoutlibDelegate 187 static boolean nGetFullPathProperties(long pathPtr, byte[] propertiesData, 188 int length) { 189 VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr); 190 191 ByteBuffer properties = ByteBuffer.wrap(propertiesData); 192 properties.order(ByteOrder.nativeOrder()); 193 194 properties.putFloat(VFullPath_Delegate.STROKE_WIDTH_INDEX * 4, path.getStrokeWidth()); 195 properties.putInt(VFullPath_Delegate.STROKE_COLOR_INDEX * 4, path.getStrokeColor()); 196 properties.putFloat(VFullPath_Delegate.STROKE_ALPHA_INDEX * 4, path.getStrokeAlpha()); 197 properties.putInt(VFullPath_Delegate.FILL_COLOR_INDEX * 4, path.getFillColor()); 198 properties.putFloat(VFullPath_Delegate.FILL_ALPHA_INDEX * 4, path.getStrokeAlpha()); 199 properties.putFloat(VFullPath_Delegate.TRIM_PATH_START_INDEX * 4, path.getTrimPathStart()); 200 properties.putFloat(VFullPath_Delegate.TRIM_PATH_END_INDEX * 4, path.getTrimPathEnd()); 201 properties.putFloat(VFullPath_Delegate.TRIM_PATH_OFFSET_INDEX * 4, 202 path.getTrimPathOffset()); 203 properties.putInt(VFullPath_Delegate.STROKE_LINE_CAP_INDEX * 4, path.getStrokeLineCap()); 204 properties.putInt(VFullPath_Delegate.STROKE_LINE_JOIN_INDEX * 4, path.getStrokeLineJoin()); 205 properties.putFloat(VFullPath_Delegate.STROKE_MITER_LIMIT_INDEX * 4, 206 path.getStrokeMiterlimit()); 207 properties.putInt(VFullPath_Delegate.FILL_TYPE_INDEX * 4, path.getFillType()); 208 209 return true; 210 } 211 212 @LayoutlibDelegate 213 static void nUpdateFullPathProperties(long pathPtr, float strokeWidth, 214 int strokeColor, float strokeAlpha, int fillColor, float fillAlpha, float trimPathStart, 215 float trimPathEnd, float trimPathOffset, float strokeMiterLimit, int strokeLineCap, 216 int strokeLineJoin, int fillType) { 217 VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr); 218 219 path.setStrokeWidth(strokeWidth); 220 path.setStrokeColor(strokeColor); 221 path.setStrokeAlpha(strokeAlpha); 222 path.setFillColor(fillColor); 223 path.setFillAlpha(fillAlpha); 224 path.setTrimPathStart(trimPathStart); 225 path.setTrimPathEnd(trimPathEnd); 226 path.setTrimPathOffset(trimPathOffset); 227 path.setStrokeMiterlimit(strokeMiterLimit); 228 path.setStrokeLineCap(strokeLineCap); 229 path.setStrokeLineJoin(strokeLineJoin); 230 path.setFillType(fillType); 231 } 232 233 @LayoutlibDelegate 234 static void nUpdateFullPathFillGradient(long pathPtr, long fillGradientPtr) { 235 VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr); 236 237 path.setFillGradient(fillGradientPtr); 238 } 239 240 @LayoutlibDelegate 241 static void nUpdateFullPathStrokeGradient(long pathPtr, long strokeGradientPtr) { 242 VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr); 243 244 path.setStrokeGradient(strokeGradientPtr); 245 } 246 247 @LayoutlibDelegate 248 static long nCreateClipPath() { 249 return addNativeObject(new VClipPath_Delegate()); 250 } 251 252 @LayoutlibDelegate 253 static long nCreateClipPath(long clipPathPtr) { 254 VClipPath_Delegate original = VNativeObject.getDelegate(clipPathPtr); 255 return addNativeObject(new VClipPath_Delegate(original)); 256 } 257 258 @LayoutlibDelegate 259 static long nCreateGroup() { 260 return addNativeObject(new VGroup_Delegate()); 261 } 262 263 @LayoutlibDelegate 264 static long nCreateGroup(long groupPtr) { 265 VGroup_Delegate original = VNativeObject.getDelegate(groupPtr); 266 return addNativeObject(new VGroup_Delegate(original, new ArrayMap<>())); 267 } 268 269 @LayoutlibDelegate 270 static void nSetName(long nodePtr, String name) { 271 VNativeObject group = VNativeObject.getDelegate(nodePtr); 272 group.setName(name); 273 } 274 275 @LayoutlibDelegate 276 static boolean nGetGroupProperties(long groupPtr, float[] propertiesData, 277 int length) { 278 VGroup_Delegate group = VNativeObject.getDelegate(groupPtr); 279 280 FloatBuffer properties = FloatBuffer.wrap(propertiesData); 281 282 properties.put(VGroup_Delegate.ROTATE_INDEX, group.getRotation()); 283 properties.put(VGroup_Delegate.PIVOT_X_INDEX, group.getPivotX()); 284 properties.put(VGroup_Delegate.PIVOT_Y_INDEX, group.getPivotY()); 285 properties.put(VGroup_Delegate.SCALE_X_INDEX, group.getScaleX()); 286 properties.put(VGroup_Delegate.SCALE_Y_INDEX, group.getScaleY()); 287 properties.put(VGroup_Delegate.TRANSLATE_X_INDEX, group.getTranslateX()); 288 properties.put(VGroup_Delegate.TRANSLATE_Y_INDEX, group.getTranslateY()); 289 290 return true; 291 } 292 @LayoutlibDelegate 293 static void nUpdateGroupProperties(long groupPtr, float rotate, float pivotX, 294 float pivotY, float scaleX, float scaleY, float translateX, float translateY) { 295 VGroup_Delegate group = VNativeObject.getDelegate(groupPtr); 296 297 group.setRotation(rotate); 298 group.setPivotX(pivotX); 299 group.setPivotY(pivotY); 300 group.setScaleX(scaleX); 301 group.setScaleY(scaleY); 302 group.setTranslateX(translateX); 303 group.setTranslateY(translateY); 304 } 305 306 @LayoutlibDelegate 307 static void nAddChild(long groupPtr, long nodePtr) { 308 VGroup_Delegate group = VNativeObject.getDelegate(groupPtr); 309 group.mChildren.add(VNativeObject.getDelegate(nodePtr)); 310 } 311 312 @LayoutlibDelegate 313 static void nSetPathString(long pathPtr, String pathString, int length) { 314 VPath_Delegate path = VNativeObject.getDelegate(pathPtr); 315 path.setPathData(PathParser_Delegate.createNodesFromPathData(pathString)); 316 } 317 318 /** 319 * The setters and getters below for paths and groups are here temporarily, and will be removed 320 * once the animation in AVD is replaced with RenderNodeAnimator, in which case the animation 321 * will modify these properties in native. By then no JNI hopping would be necessary for VD 322 * during animation, and these setters and getters will be obsolete. 323 */ 324 // Setters and getters during animation. 325 @LayoutlibDelegate 326 static float nGetRotation(long groupPtr) { 327 VGroup_Delegate group = VNativeObject.getDelegate(groupPtr); 328 return group.getRotation(); 329 } 330 331 @LayoutlibDelegate 332 static void nSetRotation(long groupPtr, float rotation) { 333 VGroup_Delegate group = VNativeObject.getDelegate(groupPtr); 334 group.setRotation(rotation); 335 } 336 337 @LayoutlibDelegate 338 static float nGetPivotX(long groupPtr) { 339 VGroup_Delegate group = VNativeObject.getDelegate(groupPtr); 340 return group.getPivotX(); 341 } 342 343 @LayoutlibDelegate 344 static void nSetPivotX(long groupPtr, float pivotX) { 345 VGroup_Delegate group = VNativeObject.getDelegate(groupPtr); 346 group.setPivotX(pivotX); 347 } 348 349 @LayoutlibDelegate 350 static float nGetPivotY(long groupPtr) { 351 VGroup_Delegate group = VNativeObject.getDelegate(groupPtr); 352 return group.getPivotY(); 353 } 354 355 @LayoutlibDelegate 356 static void nSetPivotY(long groupPtr, float pivotY) { 357 VGroup_Delegate group = VNativeObject.getDelegate(groupPtr); 358 group.setPivotY(pivotY); 359 } 360 361 @LayoutlibDelegate 362 static float nGetScaleX(long groupPtr) { 363 VGroup_Delegate group = VNativeObject.getDelegate(groupPtr); 364 return group.getScaleX(); 365 } 366 367 @LayoutlibDelegate 368 static void nSetScaleX(long groupPtr, float scaleX) { 369 VGroup_Delegate group = VNativeObject.getDelegate(groupPtr); 370 group.setScaleX(scaleX); 371 } 372 373 @LayoutlibDelegate 374 static float nGetScaleY(long groupPtr) { 375 VGroup_Delegate group = VNativeObject.getDelegate(groupPtr); 376 return group.getScaleY(); 377 } 378 379 @LayoutlibDelegate 380 static void nSetScaleY(long groupPtr, float scaleY) { 381 VGroup_Delegate group = VNativeObject.getDelegate(groupPtr); 382 group.setScaleY(scaleY); 383 } 384 385 @LayoutlibDelegate 386 static float nGetTranslateX(long groupPtr) { 387 VGroup_Delegate group = VNativeObject.getDelegate(groupPtr); 388 return group.getTranslateX(); 389 } 390 391 @LayoutlibDelegate 392 static void nSetTranslateX(long groupPtr, float translateX) { 393 VGroup_Delegate group = VNativeObject.getDelegate(groupPtr); 394 group.setTranslateX(translateX); 395 } 396 397 @LayoutlibDelegate 398 static float nGetTranslateY(long groupPtr) { 399 VGroup_Delegate group = VNativeObject.getDelegate(groupPtr); 400 return group.getTranslateY(); 401 } 402 403 @LayoutlibDelegate 404 static void nSetTranslateY(long groupPtr, float translateY) { 405 VGroup_Delegate group = VNativeObject.getDelegate(groupPtr); 406 group.setTranslateY(translateY); 407 } 408 409 @LayoutlibDelegate 410 static void nSetPathData(long pathPtr, long pathDataPtr) { 411 VPath_Delegate path = VNativeObject.getDelegate(pathPtr); 412 path.setPathData(PathParser_Delegate.getDelegate(pathDataPtr).getPathDataNodes()); 413 } 414 415 @LayoutlibDelegate 416 static float nGetStrokeWidth(long pathPtr) { 417 VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr); 418 return path.getStrokeWidth(); 419 } 420 421 @LayoutlibDelegate 422 static void nSetStrokeWidth(long pathPtr, float width) { 423 VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr); 424 path.setStrokeWidth(width); 425 } 426 427 @LayoutlibDelegate 428 static int nGetStrokeColor(long pathPtr) { 429 VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr); 430 return path.getStrokeColor(); 431 } 432 433 @LayoutlibDelegate 434 static void nSetStrokeColor(long pathPtr, int strokeColor) { 435 VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr); 436 path.setStrokeColor(strokeColor); 437 } 438 439 @LayoutlibDelegate 440 static float nGetStrokeAlpha(long pathPtr) { 441 VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr); 442 return path.getStrokeAlpha(); 443 } 444 445 @LayoutlibDelegate 446 static void nSetStrokeAlpha(long pathPtr, float alpha) { 447 VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr); 448 path.setStrokeAlpha(alpha); 449 } 450 451 @LayoutlibDelegate 452 static int nGetFillColor(long pathPtr) { 453 VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr); 454 return path.getFillColor(); 455 } 456 457 @LayoutlibDelegate 458 static void nSetFillColor(long pathPtr, int fillColor) { 459 VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr); 460 path.setFillColor(fillColor); 461 } 462 463 @LayoutlibDelegate 464 static float nGetFillAlpha(long pathPtr) { 465 VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr); 466 return path.getFillAlpha(); 467 } 468 469 @LayoutlibDelegate 470 static void nSetFillAlpha(long pathPtr, float fillAlpha) { 471 VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr); 472 path.setFillAlpha(fillAlpha); 473 } 474 475 @LayoutlibDelegate 476 static float nGetTrimPathStart(long pathPtr) { 477 VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr); 478 return path.getTrimPathStart(); 479 } 480 481 @LayoutlibDelegate 482 static void nSetTrimPathStart(long pathPtr, float trimPathStart) { 483 VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr); 484 path.setTrimPathStart(trimPathStart); 485 } 486 487 @LayoutlibDelegate 488 static float nGetTrimPathEnd(long pathPtr) { 489 VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr); 490 return path.getTrimPathEnd(); 491 } 492 493 @LayoutlibDelegate 494 static void nSetTrimPathEnd(long pathPtr, float trimPathEnd) { 495 VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr); 496 path.setTrimPathEnd(trimPathEnd); 497 } 498 499 @LayoutlibDelegate 500 static float nGetTrimPathOffset(long pathPtr) { 501 VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr); 502 return path.getTrimPathOffset(); 503 } 504 505 @LayoutlibDelegate 506 static void nSetTrimPathOffset(long pathPtr, float trimPathOffset) { 507 VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr); 508 path.setTrimPathOffset(trimPathOffset); 509 } 510 511 /** 512 * Base class for all the internal Delegates that does two functions: 513 * <ol> 514 * <li>Serves as base class to store all the delegates in one {@link DelegateManager} 515 * <li>Provides setName for all the classes. {@link VPathRenderer_Delegate} does actually 516 * not need it 517 * </ol> 518 */ 519 abstract static class VNativeObject { 520 long mNativePtr = 0; 521 522 @NonNull 523 static <T> T getDelegate(long nativePtr) { 524 //noinspection unchecked 525 T vNativeObject = (T) sPathManager.getDelegate(nativePtr); 526 527 assert vNativeObject != null; 528 return vNativeObject; 529 } 530 531 abstract void setName(String name); 532 533 void setNativePtr(long nativePtr) { 534 mNativePtr = nativePtr; 535 } 536 537 /** 538 * Method to explicitly dispose native objects 539 */ 540 void dispose() { 541 } 542 } 543 544 private static class VClipPath_Delegate extends VPath_Delegate { 545 private VClipPath_Delegate() { 546 // Empty constructor. 547 } 548 549 private VClipPath_Delegate(VClipPath_Delegate copy) { 550 super(copy); 551 } 552 553 @Override 554 public boolean isClipPath() { 555 return true; 556 } 557 } 558 559 static class VFullPath_Delegate extends VPath_Delegate { 560 // These constants need to be kept in sync with their values in VectorDrawable.VFullPath 561 private static final int STROKE_WIDTH_INDEX = 0; 562 private static final int STROKE_COLOR_INDEX = 1; 563 private static final int STROKE_ALPHA_INDEX = 2; 564 private static final int FILL_COLOR_INDEX = 3; 565 private static final int FILL_ALPHA_INDEX = 4; 566 private static final int TRIM_PATH_START_INDEX = 5; 567 private static final int TRIM_PATH_END_INDEX = 6; 568 private static final int TRIM_PATH_OFFSET_INDEX = 7; 569 private static final int STROKE_LINE_CAP_INDEX = 8; 570 private static final int STROKE_LINE_JOIN_INDEX = 9; 571 private static final int STROKE_MITER_LIMIT_INDEX = 10; 572 private static final int FILL_TYPE_INDEX = 11; 573 574 private static final int LINECAP_BUTT = 0; 575 private static final int LINECAP_ROUND = 1; 576 private static final int LINECAP_SQUARE = 2; 577 578 private static final int LINEJOIN_MITER = 0; 579 private static final int LINEJOIN_ROUND = 1; 580 private static final int LINEJOIN_BEVEL = 2; 581 582 @NonNull 583 public Consumer<Float> getFloatPropertySetter(int propertyIdx) { 584 switch (propertyIdx) { 585 case STROKE_WIDTH_INDEX: 586 return this::setStrokeWidth; 587 case STROKE_ALPHA_INDEX: 588 return this::setStrokeAlpha; 589 case FILL_ALPHA_INDEX: 590 return this::setFillAlpha; 591 case TRIM_PATH_START_INDEX: 592 return this::setTrimPathStart; 593 case TRIM_PATH_END_INDEX: 594 return this::setTrimPathEnd; 595 case TRIM_PATH_OFFSET_INDEX: 596 return this::setTrimPathOffset; 597 } 598 599 assert false : ("Invalid VFullPath_Delegate property index " + propertyIdx); 600 return t -> {}; 601 } 602 603 @NonNull 604 public Consumer<Integer> getIntPropertySetter(int propertyIdx) { 605 switch (propertyIdx) { 606 case STROKE_COLOR_INDEX: 607 return this::setStrokeColor; 608 case FILL_COLOR_INDEX: 609 return this::setFillColor; 610 } 611 612 assert false : ("Invalid VFullPath_Delegate property index " + propertyIdx); 613 return t -> {}; 614 } 615 616 ///////////////////////////////////////////////////// 617 // Variables below need to be copied (deep copy if applicable) for mutation. 618 619 int mStrokeColor = Color.TRANSPARENT; 620 float mStrokeWidth = 0; 621 622 int mFillColor = Color.TRANSPARENT; 623 long mStrokeGradient = 0; 624 long mFillGradient = 0; 625 float mStrokeAlpha = 1.0f; 626 float mFillAlpha = 1.0f; 627 float mTrimPathStart = 0; 628 float mTrimPathEnd = 1; 629 float mTrimPathOffset = 0; 630 631 Cap mStrokeLineCap = BUTT; 632 Join mStrokeLineJoin = MITER; 633 float mStrokeMiterlimit = 4; 634 635 int mFillType = 0; // WINDING(0) is the default value. See Path.FillType 636 637 private VFullPath_Delegate() { 638 // Empty constructor. 639 } 640 641 private VFullPath_Delegate(VFullPath_Delegate copy) { 642 super(copy); 643 644 mStrokeColor = copy.mStrokeColor; 645 mStrokeWidth = copy.mStrokeWidth; 646 mStrokeAlpha = copy.mStrokeAlpha; 647 mFillColor = copy.mFillColor; 648 mFillAlpha = copy.mFillAlpha; 649 mTrimPathStart = copy.mTrimPathStart; 650 mTrimPathEnd = copy.mTrimPathEnd; 651 mTrimPathOffset = copy.mTrimPathOffset; 652 653 mStrokeLineCap = copy.mStrokeLineCap; 654 mStrokeLineJoin = copy.mStrokeLineJoin; 655 mStrokeMiterlimit = copy.mStrokeMiterlimit; 656 657 mStrokeGradient = copy.mStrokeGradient; 658 mFillGradient = copy.mFillGradient; 659 mFillType = copy.mFillType; 660 } 661 662 private int getStrokeLineCap() { 663 switch (mStrokeLineCap) { 664 case BUTT: 665 return LINECAP_BUTT; 666 case ROUND: 667 return LINECAP_ROUND; 668 case SQUARE: 669 return LINECAP_SQUARE; 670 default: 671 assert false; 672 } 673 674 return -1; 675 } 676 677 private void setStrokeLineCap(int cap) { 678 switch (cap) { 679 case LINECAP_BUTT: 680 mStrokeLineCap = BUTT; 681 break; 682 case LINECAP_ROUND: 683 mStrokeLineCap = ROUND; 684 break; 685 case LINECAP_SQUARE: 686 mStrokeLineCap = SQUARE; 687 break; 688 default: 689 assert false; 690 } 691 } 692 693 private int getStrokeLineJoin() { 694 switch (mStrokeLineJoin) { 695 case MITER: 696 return LINEJOIN_MITER; 697 case ROUND: 698 return LINEJOIN_ROUND; 699 case BEVEL: 700 return LINEJOIN_BEVEL; 701 default: 702 assert false; 703 } 704 705 return -1; 706 } 707 708 private void setStrokeLineJoin(int join) { 709 switch (join) { 710 case LINEJOIN_BEVEL: 711 mStrokeLineJoin = BEVEL; 712 break; 713 case LINEJOIN_MITER: 714 mStrokeLineJoin = MITER; 715 break; 716 case LINEJOIN_ROUND: 717 mStrokeLineJoin = Join.ROUND; 718 break; 719 default: 720 assert false; 721 } 722 } 723 724 private int getStrokeColor() { 725 return mStrokeColor; 726 } 727 728 private void setStrokeColor(int strokeColor) { 729 mStrokeColor = strokeColor; 730 } 731 732 private float getStrokeWidth() { 733 return mStrokeWidth; 734 } 735 736 private void setStrokeWidth(float strokeWidth) { 737 mStrokeWidth = strokeWidth; 738 } 739 740 private float getStrokeAlpha() { 741 return mStrokeAlpha; 742 } 743 744 private void setStrokeAlpha(float strokeAlpha) { 745 mStrokeAlpha = strokeAlpha; 746 } 747 748 private int getFillColor() { 749 return mFillColor; 750 } 751 752 private void setFillColor(int fillColor) { 753 mFillColor = fillColor; 754 } 755 756 private float getFillAlpha() { 757 return mFillAlpha; 758 } 759 760 private void setFillAlpha(float fillAlpha) { 761 mFillAlpha = fillAlpha; 762 } 763 764 private float getTrimPathStart() { 765 return mTrimPathStart; 766 } 767 768 private void setTrimPathStart(float trimPathStart) { 769 mTrimPathStart = trimPathStart; 770 } 771 772 private float getTrimPathEnd() { 773 return mTrimPathEnd; 774 } 775 776 private void setTrimPathEnd(float trimPathEnd) { 777 mTrimPathEnd = trimPathEnd; 778 } 779 780 private float getTrimPathOffset() { 781 return mTrimPathOffset; 782 } 783 784 private void setTrimPathOffset(float trimPathOffset) { 785 mTrimPathOffset = trimPathOffset; 786 } 787 788 private void setStrokeMiterlimit(float limit) { 789 mStrokeMiterlimit = limit; 790 } 791 792 private float getStrokeMiterlimit() { 793 return mStrokeMiterlimit; 794 } 795 796 private void setStrokeGradient(long gradientPtr) { 797 mStrokeGradient = gradientPtr; 798 } 799 800 private void setFillGradient(long gradientPtr) { 801 mFillGradient = gradientPtr; 802 } 803 804 private void setFillType(int fillType) { 805 mFillType = fillType; 806 } 807 808 private int getFillType() { 809 return mFillType; 810 } 811 } 812 813 static class VGroup_Delegate extends VNativeObject { 814 // This constants need to be kept in sync with their definitions in VectorDrawable.Group 815 private static final int ROTATE_INDEX = 0; 816 private static final int PIVOT_X_INDEX = 1; 817 private static final int PIVOT_Y_INDEX = 2; 818 private static final int SCALE_X_INDEX = 3; 819 private static final int SCALE_Y_INDEX = 4; 820 private static final int TRANSLATE_X_INDEX = 5; 821 private static final int TRANSLATE_Y_INDEX = 6; 822 823 public Consumer<Float> getPropertySetter(int propertyIdx) { 824 switch (propertyIdx) { 825 case ROTATE_INDEX: 826 return this::setRotation; 827 case PIVOT_X_INDEX: 828 return this::setPivotX; 829 case PIVOT_Y_INDEX: 830 return this::setPivotY; 831 case SCALE_X_INDEX: 832 return this::setScaleX; 833 case SCALE_Y_INDEX: 834 return this::setScaleY; 835 case TRANSLATE_X_INDEX: 836 return this::setTranslateX; 837 case TRANSLATE_Y_INDEX: 838 return this::setTranslateY; 839 } 840 841 assert false : ("Invalid VGroup_Delegate property index " + propertyIdx); 842 return t -> {}; 843 } 844 845 ///////////////////////////////////////////////////// 846 // Variables below need to be copied (deep copy if applicable) for mutation. 847 final ArrayList<Object> mChildren = new ArrayList<>(); 848 // mStackedMatrix is only used temporarily when drawing, it combines all 849 // the parents' local matrices with the current one. 850 private final Matrix mStackedMatrix = new Matrix(); 851 // mLocalMatrix is updated based on the update of transformation information, 852 // either parsed from the XML or by animation. 853 private final Matrix mLocalMatrix = new Matrix(); 854 private float mRotate = 0; 855 private float mPivotX = 0; 856 private float mPivotY = 0; 857 private float mScaleX = 1; 858 private float mScaleY = 1; 859 private float mTranslateX = 0; 860 private float mTranslateY = 0; 861 private int mChangingConfigurations; 862 private String mGroupName = null; 863 864 private VGroup_Delegate(VGroup_Delegate copy, ArrayMap<String, Object> targetsMap) { 865 mRotate = copy.mRotate; 866 mPivotX = copy.mPivotX; 867 mPivotY = copy.mPivotY; 868 mScaleX = copy.mScaleX; 869 mScaleY = copy.mScaleY; 870 mTranslateX = copy.mTranslateX; 871 mTranslateY = copy.mTranslateY; 872 mGroupName = copy.mGroupName; 873 mChangingConfigurations = copy.mChangingConfigurations; 874 if (mGroupName != null) { 875 targetsMap.put(mGroupName, this); 876 } 877 878 mLocalMatrix.set(copy.mLocalMatrix); 879 } 880 881 private VGroup_Delegate() { 882 } 883 884 private void updateLocalMatrix() { 885 // The order we apply is the same as the 886 // RenderNode.cpp::applyViewPropertyTransforms(). 887 mLocalMatrix.reset(); 888 mLocalMatrix.postTranslate(-mPivotX, -mPivotY); 889 mLocalMatrix.postScale(mScaleX, mScaleY); 890 mLocalMatrix.postRotate(mRotate, 0, 0); 891 mLocalMatrix.postTranslate(mTranslateX + mPivotX, mTranslateY + mPivotY); 892 } 893 894 /* Setters and Getters, used by animator from AnimatedVectorDrawable. */ 895 private float getRotation() { 896 return mRotate; 897 } 898 899 private void setRotation(float rotation) { 900 if (rotation != mRotate) { 901 mRotate = rotation; 902 updateLocalMatrix(); 903 } 904 } 905 906 private float getPivotX() { 907 return mPivotX; 908 } 909 910 private void setPivotX(float pivotX) { 911 if (pivotX != mPivotX) { 912 mPivotX = pivotX; 913 updateLocalMatrix(); 914 } 915 } 916 917 private float getPivotY() { 918 return mPivotY; 919 } 920 921 private void setPivotY(float pivotY) { 922 if (pivotY != mPivotY) { 923 mPivotY = pivotY; 924 updateLocalMatrix(); 925 } 926 } 927 928 private float getScaleX() { 929 return mScaleX; 930 } 931 932 private void setScaleX(float scaleX) { 933 if (scaleX != mScaleX) { 934 mScaleX = scaleX; 935 updateLocalMatrix(); 936 } 937 } 938 939 private float getScaleY() { 940 return mScaleY; 941 } 942 943 private void setScaleY(float scaleY) { 944 if (scaleY != mScaleY) { 945 mScaleY = scaleY; 946 updateLocalMatrix(); 947 } 948 } 949 950 private float getTranslateX() { 951 return mTranslateX; 952 } 953 954 private void setTranslateX(float translateX) { 955 if (translateX != mTranslateX) { 956 mTranslateX = translateX; 957 updateLocalMatrix(); 958 } 959 } 960 961 private float getTranslateY() { 962 return mTranslateY; 963 } 964 965 private void setTranslateY(float translateY) { 966 if (translateY != mTranslateY) { 967 mTranslateY = translateY; 968 updateLocalMatrix(); 969 } 970 } 971 972 @Override 973 public void setName(String name) { 974 mGroupName = name; 975 } 976 977 @Override 978 protected void dispose() { 979 mChildren.stream().filter(child -> child instanceof VNativeObject).forEach(child 980 -> { 981 VNativeObject nativeObject = (VNativeObject) child; 982 if (nativeObject.mNativePtr != 0) { 983 sPathManager.removeJavaReferenceFor(nativeObject.mNativePtr); 984 nativeObject.mNativePtr = 0; 985 } 986 nativeObject.dispose(); 987 }); 988 mChildren.clear(); 989 } 990 991 @Override 992 protected void finalize() throws Throwable { 993 super.finalize(); 994 } 995 } 996 997 public static class VPath_Delegate extends VNativeObject { 998 protected PathParser_Delegate.PathDataNode[] mNodes = null; 999 String mPathName; 1000 int mChangingConfigurations; 1001 1002 public VPath_Delegate() { 1003 // Empty constructor. 1004 } 1005 1006 public VPath_Delegate(VPath_Delegate copy) { 1007 mPathName = copy.mPathName; 1008 mChangingConfigurations = copy.mChangingConfigurations; 1009 mNodes = copy.mNodes != null ? PathParser_Delegate.deepCopyNodes(copy.mNodes) : null; 1010 } 1011 1012 public void toPath(Path path) { 1013 path.reset(); 1014 if (mNodes != null) { 1015 PathParser_Delegate.PathDataNode.nodesToPath(mNodes, 1016 Path_Delegate.getDelegate(path.mNativePath)); 1017 } 1018 } 1019 1020 @Override 1021 public void setName(String name) { 1022 mPathName = name; 1023 } 1024 1025 public boolean isClipPath() { 1026 return false; 1027 } 1028 1029 private void setPathData(PathParser_Delegate.PathDataNode[] nodes) { 1030 if (!PathParser_Delegate.canMorph(mNodes, nodes)) { 1031 // This should not happen in the middle of animation. 1032 mNodes = PathParser_Delegate.deepCopyNodes(nodes); 1033 } else { 1034 PathParser_Delegate.updateNodes(mNodes, nodes); 1035 } 1036 } 1037 1038 @Override 1039 void dispose() { 1040 mNodes = null; 1041 } 1042 } 1043 1044 static class VPathRenderer_Delegate extends VNativeObject { 1045 /* Right now the internal data structure is organized as a tree. 1046 * Each node can be a group node, or a path. 1047 * A group node can have groups or paths as children, but a path node has 1048 * no children. 1049 * One example can be: 1050 * Root Group 1051 * / | \ 1052 * Group Path Group 1053 * / \ | 1054 * Path Path Path 1055 * 1056 */ 1057 // Variables that only used temporarily inside the draw() call, so there 1058 // is no need for deep copying. 1059 private final Path mPath; 1060 private final Path mRenderPath; 1061 private final Matrix mFinalPathMatrix = new Matrix(); 1062 private final long mRootGroupPtr; 1063 private float mViewportWidth = 0; 1064 private float mViewportHeight = 0; 1065 private float mRootAlpha = 1.0f; 1066 private Paint mStrokePaint; 1067 private Paint mFillPaint; 1068 private PathMeasure mPathMeasure; 1069 private boolean mAntiAlias = true; 1070 1071 private VPathRenderer_Delegate(long rootGroupPtr) { 1072 mRootGroupPtr = rootGroupPtr; 1073 mPath = new Path(); 1074 mRenderPath = new Path(); 1075 } 1076 1077 private VPathRenderer_Delegate(VPathRenderer_Delegate rendererToCopy, 1078 long rootGroupPtr) { 1079 this(rootGroupPtr); 1080 mViewportWidth = rendererToCopy.mViewportWidth; 1081 mViewportHeight = rendererToCopy.mViewportHeight; 1082 mRootAlpha = rendererToCopy.mRootAlpha; 1083 } 1084 1085 private float getRootAlpha() { 1086 return mRootAlpha; 1087 } 1088 1089 void setRootAlpha(float alpha) { 1090 mRootAlpha = alpha; 1091 } 1092 1093 private void drawGroupTree(VGroup_Delegate currentGroup, Matrix currentMatrix, 1094 long canvasPtr, int w, int h, long filterPtr) { 1095 // Calculate current group's matrix by preConcat the parent's and 1096 // and the current one on the top of the stack. 1097 // Basically the Mfinal = Mviewport * M0 * M1 * M2; 1098 // Mi the local matrix at level i of the group tree. 1099 currentGroup.mStackedMatrix.set(currentMatrix); 1100 currentGroup.mStackedMatrix.preConcat(currentGroup.mLocalMatrix); 1101 1102 // Save the current clip information, which is local to this group. 1103 Canvas_Delegate.nSave(canvasPtr, MATRIX_SAVE_FLAG | CLIP_SAVE_FLAG); 1104 // Draw the group tree in the same order as the XML file. 1105 for (int i = 0; i < currentGroup.mChildren.size(); i++) { 1106 Object child = currentGroup.mChildren.get(i); 1107 if (child instanceof VGroup_Delegate) { 1108 VGroup_Delegate childGroup = (VGroup_Delegate) child; 1109 drawGroupTree(childGroup, currentGroup.mStackedMatrix, 1110 canvasPtr, w, h, filterPtr); 1111 } else if (child instanceof VPath_Delegate) { 1112 VPath_Delegate childPath = (VPath_Delegate) child; 1113 drawPath(currentGroup, childPath, canvasPtr, w, h, filterPtr); 1114 } 1115 } 1116 Canvas_Delegate.nRestore(canvasPtr); 1117 } 1118 1119 public void draw(long canvasPtr, long filterPtr, int w, int h) { 1120 // Traverse the tree in pre-order to draw. 1121 drawGroupTree(VNativeObject.getDelegate(mRootGroupPtr), Matrix.IDENTITY_MATRIX, canvasPtr, w, h, filterPtr); 1122 } 1123 1124 private void drawPath(VGroup_Delegate VGroup, VPath_Delegate VPath, long canvasPtr, 1125 int w, 1126 int h, 1127 long filterPtr) { 1128 final float scaleX = w / mViewportWidth; 1129 final float scaleY = h / mViewportHeight; 1130 final float minScale = Math.min(scaleX, scaleY); 1131 final Matrix groupStackedMatrix = VGroup.mStackedMatrix; 1132 1133 mFinalPathMatrix.set(groupStackedMatrix); 1134 mFinalPathMatrix.postScale(scaleX, scaleY); 1135 1136 final float matrixScale = getMatrixScale(groupStackedMatrix); 1137 if (matrixScale == 0) { 1138 // When either x or y is scaled to 0, we don't need to draw anything. 1139 return; 1140 } 1141 VPath.toPath(mPath); 1142 final Path path = mPath; 1143 1144 mRenderPath.reset(); 1145 1146 if (VPath.isClipPath()) { 1147 mRenderPath.addPath(path, mFinalPathMatrix); 1148 Canvas_Delegate.nClipPath(canvasPtr, mRenderPath.mNativePath, Op 1149 .INTERSECT.nativeInt); 1150 } else { 1151 VFullPath_Delegate fullPath = (VFullPath_Delegate) VPath; 1152 if (fullPath.mTrimPathStart != 0.0f || fullPath.mTrimPathEnd != 1.0f) { 1153 float start = (fullPath.mTrimPathStart + fullPath.mTrimPathOffset) % 1.0f; 1154 float end = (fullPath.mTrimPathEnd + fullPath.mTrimPathOffset) % 1.0f; 1155 1156 if (mPathMeasure == null) { 1157 mPathMeasure = new PathMeasure(); 1158 } 1159 mPathMeasure.setPath(mPath, false); 1160 1161 float len = mPathMeasure.getLength(); 1162 start = start * len; 1163 end = end * len; 1164 path.reset(); 1165 if (start > end) { 1166 mPathMeasure.getSegment(start, len, path, true); 1167 mPathMeasure.getSegment(0f, end, path, true); 1168 } else { 1169 mPathMeasure.getSegment(start, end, path, true); 1170 } 1171 path.rLineTo(0, 0); // fix bug in measure 1172 } 1173 mRenderPath.addPath(path, mFinalPathMatrix); 1174 1175 if (fullPath.mFillColor != Color.TRANSPARENT) { 1176 if (mFillPaint == null) { 1177 mFillPaint = new Paint(); 1178 mFillPaint.setStyle(Style.FILL); 1179 mFillPaint.setAntiAlias(mAntiAlias); 1180 } 1181 1182 final Paint fillPaint = mFillPaint; 1183 fillPaint.setColor(applyAlpha(applyAlpha(fullPath.mFillColor, fullPath 1184 .mFillAlpha), getRootAlpha())); 1185 Paint_Delegate fillPaintDelegate = Paint_Delegate.getDelegate(fillPaint 1186 .getNativeInstance()); 1187 // mFillPaint can not be null at this point so we will have a delegate 1188 assert fillPaintDelegate != null; 1189 fillPaintDelegate.setColorFilter(filterPtr); 1190 1191 Shader_Delegate shaderDelegate = 1192 Shader_Delegate.getDelegate(fullPath.mFillGradient); 1193 if (shaderDelegate != null) { 1194 // If there is a shader, apply the local transformation to make sure 1195 // the gradient is transformed to match the viewport 1196 shaderDelegate.setLocalMatrix(mFinalPathMatrix.native_instance); 1197 } 1198 1199 fillPaintDelegate.setShader(fullPath.mFillGradient); 1200 Path_Delegate.nSetFillType(mRenderPath.mNativePath, fullPath.mFillType); 1201 BaseCanvas_Delegate.nDrawPath(canvasPtr, mRenderPath.mNativePath, fillPaint 1202 .getNativeInstance()); 1203 if (shaderDelegate != null) { 1204 // Remove the local matrix 1205 shaderDelegate.setLocalMatrix(0); 1206 } 1207 } 1208 1209 if (fullPath.mStrokeColor != Color.TRANSPARENT) { 1210 if (mStrokePaint == null) { 1211 mStrokePaint = new Paint(); 1212 mStrokePaint.setStyle(Style.STROKE); 1213 mStrokePaint.setAntiAlias(mAntiAlias); 1214 } 1215 1216 final Paint strokePaint = mStrokePaint; 1217 if (fullPath.mStrokeLineJoin != null) { 1218 strokePaint.setStrokeJoin(fullPath.mStrokeLineJoin); 1219 } 1220 1221 if (fullPath.mStrokeLineCap != null) { 1222 strokePaint.setStrokeCap(fullPath.mStrokeLineCap); 1223 } 1224 1225 strokePaint.setStrokeMiter(fullPath.mStrokeMiterlimit); 1226 strokePaint.setColor(applyAlpha(applyAlpha(fullPath.mStrokeColor, fullPath 1227 .mStrokeAlpha), getRootAlpha())); 1228 Paint_Delegate strokePaintDelegate = Paint_Delegate.getDelegate(strokePaint 1229 .getNativeInstance()); 1230 // mStrokePaint can not be null at this point so we will have a delegate 1231 assert strokePaintDelegate != null; 1232 strokePaintDelegate.setColorFilter(filterPtr); 1233 final float finalStrokeScale = minScale * matrixScale; 1234 strokePaint.setStrokeWidth(fullPath.mStrokeWidth * finalStrokeScale); 1235 strokePaintDelegate.setShader(fullPath.mStrokeGradient); 1236 BaseCanvas_Delegate.nDrawPath(canvasPtr, mRenderPath.mNativePath, strokePaint 1237 .getNativeInstance()); 1238 } 1239 } 1240 } 1241 1242 private float getMatrixScale(Matrix groupStackedMatrix) { 1243 // Given unit vectors A = (0, 1) and B = (1, 0). 1244 // After matrix mapping, we got A' and B'. Let theta = the angel b/t A' and B'. 1245 // Therefore, the final scale we want is min(|A'| * sin(theta), |B'| * sin(theta)), 1246 // which is (|A'| * |B'| * sin(theta)) / max (|A'|, |B'|); 1247 // If max (|A'|, |B'|) = 0, that means either x or y has a scale of 0. 1248 // 1249 // For non-skew case, which is most of the cases, matrix scale is computing exactly the 1250 // scale on x and y axis, and take the minimal of these two. 1251 // For skew case, an unit square will mapped to a parallelogram. And this function will 1252 // return the minimal height of the 2 bases. 1253 float[] unitVectors = new float[]{0, 1, 1, 0}; 1254 groupStackedMatrix.mapVectors(unitVectors); 1255 float scaleX = MathUtils.mag(unitVectors[0], unitVectors[1]); 1256 float scaleY = MathUtils.mag(unitVectors[2], unitVectors[3]); 1257 float crossProduct = MathUtils.cross(unitVectors[0], unitVectors[1], 1258 unitVectors[2], unitVectors[3]); 1259 float maxScale = MathUtils.max(scaleX, scaleY); 1260 1261 float matrixScale = 0; 1262 if (maxScale > 0) { 1263 matrixScale = MathUtils.abs(crossProduct) / maxScale; 1264 } 1265 if (DBG_VECTOR_DRAWABLE) { 1266 Log.d(LOGTAG, "Scale x " + scaleX + " y " + scaleY + " final " + matrixScale); 1267 } 1268 return matrixScale; 1269 } 1270 1271 private void setAntiAlias(boolean aa) { 1272 mAntiAlias = aa; 1273 } 1274 1275 @Override 1276 public void setName(String name) { 1277 } 1278 1279 @Override 1280 protected void finalize() throws Throwable { 1281 // The mRootGroupPtr is not explicitly freed by anything in the VectorDrawable so we 1282 // need to free it here. 1283 VNativeObject nativeObject = sPathManager.getDelegate(mRootGroupPtr); 1284 sPathManager.removeJavaReferenceFor(mRootGroupPtr); 1285 assert nativeObject != null; 1286 nativeObject.dispose(); 1287 1288 super.finalize(); 1289 } 1290 } 1291 } 1292