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