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