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