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