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