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