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