Home | History | Annotate | Download | only in drawable
      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 
     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      */
     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 
     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
    104     static long nCreateTree(long rootGroupPtr) {
    105         return addNativeObject(new VPathRenderer_Delegate(rootGroupPtr));
    106     }
    107 
    108     @LayoutlibDelegate
    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
    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
    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
    132     static float nGetRootAlpha(long rendererPtr) {
    133         VPathRenderer_Delegate nativePathRenderer = VNativeObject.getDelegate(rendererPtr);
    134 
    135         return nativePathRenderer.getRootAlpha();
    136     }
    137 
    138     @LayoutlibDelegate
    139     static void nSetAntiAlias(long rendererPtr, boolean aa) {
    140         VPathRenderer_Delegate nativePathRenderer = VNativeObject.getDelegate(rendererPtr);
    141         nativePathRenderer.setAntiAlias(aa);
    142     }
    143 
    144     @LayoutlibDelegate
    145     static void nSetAllowCaching(long rendererPtr, boolean allowCaching) {
    146         // ignored
    147     }
    148 
    149     @LayoutlibDelegate
    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
    177     static long nCreateFullPath() {
    178         return addNativeObject(new VFullPath_Delegate());
    179     }
    180 
    181     @LayoutlibDelegate
    182     static long nCreateFullPath(long nativeFullPathPtr) {
    183         VFullPath_Delegate original = VNativeObject.getDelegate(nativeFullPathPtr);
    184         return addNativeObject(new VFullPath_Delegate(original));
    185     }
    186 
    187     @LayoutlibDelegate
    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
    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
    235     static void nUpdateFullPathFillGradient(long pathPtr, long fillGradientPtr) {
    236         VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
    237 
    238         path.setFillGradient(fillGradientPtr);
    239     }
    240 
    241     @LayoutlibDelegate
    242     static void nUpdateFullPathStrokeGradient(long pathPtr, long strokeGradientPtr) {
    243         VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
    244 
    245         path.setStrokeGradient(strokeGradientPtr);
    246     }
    247 
    248     @LayoutlibDelegate
    249     static long nCreateClipPath() {
    250         return addNativeObject(new VClipPath_Delegate());
    251     }
    252 
    253     @LayoutlibDelegate
    254     static long nCreateClipPath(long clipPathPtr) {
    255         VClipPath_Delegate original = VNativeObject.getDelegate(clipPathPtr);
    256         return addNativeObject(new VClipPath_Delegate(original));
    257     }
    258 
    259     @LayoutlibDelegate
    260     static long nCreateGroup() {
    261         return addNativeObject(new VGroup_Delegate());
    262     }
    263 
    264     @LayoutlibDelegate
    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
    271     static void nSetName(long nodePtr, String name) {
    272         VNativeObject group = VNativeObject.getDelegate(nodePtr);
    273         group.setName(name);
    274     }
    275 
    276     @LayoutlibDelegate
    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
    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
    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
    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
    327     static float nGetRotation(long groupPtr) {
    328         VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
    329         return group.getRotation();
    330     }
    331 
    332     @LayoutlibDelegate
    333     static void nSetRotation(long groupPtr, float rotation) {
    334         VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
    335         group.setRotation(rotation);
    336     }
    337 
    338     @LayoutlibDelegate
    339     static float nGetPivotX(long groupPtr) {
    340         VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
    341         return group.getPivotX();
    342     }
    343 
    344     @LayoutlibDelegate
    345     static void nSetPivotX(long groupPtr, float pivotX) {
    346         VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
    347         group.setPivotX(pivotX);
    348     }
    349 
    350     @LayoutlibDelegate
    351     static float nGetPivotY(long groupPtr) {
    352         VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
    353         return group.getPivotY();
    354     }
    355 
    356     @LayoutlibDelegate
    357     static void nSetPivotY(long groupPtr, float pivotY) {
    358         VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
    359         group.setPivotY(pivotY);
    360     }
    361 
    362     @LayoutlibDelegate
    363     static float nGetScaleX(long groupPtr) {
    364         VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
    365         return group.getScaleX();
    366     }
    367 
    368     @LayoutlibDelegate
    369     static void nSetScaleX(long groupPtr, float scaleX) {
    370         VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
    371         group.setScaleX(scaleX);
    372     }
    373 
    374     @LayoutlibDelegate
    375     static float nGetScaleY(long groupPtr) {
    376         VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
    377         return group.getScaleY();
    378     }
    379 
    380     @LayoutlibDelegate
    381     static void nSetScaleY(long groupPtr, float scaleY) {
    382         VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
    383         group.setScaleY(scaleY);
    384     }
    385 
    386     @LayoutlibDelegate
    387     static float nGetTranslateX(long groupPtr) {
    388         VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
    389         return group.getTranslateX();
    390     }
    391 
    392     @LayoutlibDelegate
    393     static void nSetTranslateX(long groupPtr, float translateX) {
    394         VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
    395         group.setTranslateX(translateX);
    396     }
    397 
    398     @LayoutlibDelegate
    399     static float nGetTranslateY(long groupPtr) {
    400         VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
    401         return group.getTranslateY();
    402     }
    403 
    404     @LayoutlibDelegate
    405     static void nSetTranslateY(long groupPtr, float translateY) {
    406         VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
    407         group.setTranslateY(translateY);
    408     }
    409 
    410     @LayoutlibDelegate
    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
    417     static float nGetStrokeWidth(long pathPtr) {
    418         VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
    419         return path.getStrokeWidth();
    420     }
    421 
    422     @LayoutlibDelegate
    423     static void nSetStrokeWidth(long pathPtr, float width) {
    424         VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
    425         path.setStrokeWidth(width);
    426     }
    427 
    428     @LayoutlibDelegate
    429     static int nGetStrokeColor(long pathPtr) {
    430         VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
    431         return path.getStrokeColor();
    432     }
    433 
    434     @LayoutlibDelegate
    435     static void nSetStrokeColor(long pathPtr, int strokeColor) {
    436         VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
    437         path.setStrokeColor(strokeColor);
    438     }
    439 
    440     @LayoutlibDelegate
    441     static float nGetStrokeAlpha(long pathPtr) {
    442         VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
    443         return path.getStrokeAlpha();
    444     }
    445 
    446     @LayoutlibDelegate
    447     static void nSetStrokeAlpha(long pathPtr, float alpha) {
    448         VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
    449         path.setStrokeAlpha(alpha);
    450     }
    451 
    452     @LayoutlibDelegate
    453     static int nGetFillColor(long pathPtr) {
    454         VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
    455         return path.getFillColor();
    456     }
    457 
    458     @LayoutlibDelegate
    459     static void nSetFillColor(long pathPtr, int fillColor) {
    460         VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
    461         path.setFillColor(fillColor);
    462     }
    463 
    464     @LayoutlibDelegate
    465     static float nGetFillAlpha(long pathPtr) {
    466         VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
    467         return path.getFillAlpha();
    468     }
    469 
    470     @LayoutlibDelegate
    471     static void nSetFillAlpha(long pathPtr, float fillAlpha) {
    472         VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
    473         path.setFillAlpha(fillAlpha);
    474     }
    475 
    476     @LayoutlibDelegate
    477     static float nGetTrimPathStart(long pathPtr) {
    478         VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
    479         return path.getTrimPathStart();
    480     }
    481 
    482     @LayoutlibDelegate
    483     static void nSetTrimPathStart(long pathPtr, float trimPathStart) {
    484         VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
    485         path.setTrimPathStart(trimPathStart);
    486     }
    487 
    488     @LayoutlibDelegate
    489     static float nGetTrimPathEnd(long pathPtr) {
    490         VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
    491         return path.getTrimPathEnd();
    492     }
    493 
    494     @LayoutlibDelegate
    495     static void nSetTrimPathEnd(long pathPtr, float trimPathEnd) {
    496         VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
    497         path.setTrimPathEnd(trimPathEnd);
    498     }
    499 
    500     @LayoutlibDelegate
    501     static float nGetTrimPathOffset(long pathPtr) {
    502         VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
    503         return path.getTrimPathOffset();
    504     }
    505 
    506     @LayoutlibDelegate
    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
    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 
    532         abstract void setName(String name);
    533 
    534         void setNativePtr(long nativePtr) {
    535             mNativePtr = nativePtr;
    536         }
    537 
    538         /**
    539          * Method to explicitly dispose native objects
    540          */
    541         void dispose() {
    542         }
    543     }
    544 
    545     private static class VClipPath_Delegate extends VPath_Delegate {
    546         private VClipPath_Delegate() {
    547             // Empty constructor.
    548         }
    549 
    550         private VClipPath_Delegate(VClipPath_Delegate copy) {
    551             super(copy);
    552         }
    553 
    554         @Override
    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
    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
    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 
    638         private VFullPath_Delegate() {
    639             // Empty constructor.
    640         }
    641 
    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 
    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 
    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 
    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 
    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 
    725         private int getStrokeColor() {
    726             return mStrokeColor;
    727         }
    728 
    729         private void setStrokeColor(int strokeColor) {
    730             mStrokeColor = strokeColor;
    731         }
    732 
    733         private float getStrokeWidth() {
    734             return mStrokeWidth;
    735         }
    736 
    737         private void setStrokeWidth(float strokeWidth) {
    738             mStrokeWidth = strokeWidth;
    739         }
    740 
    741         private float getStrokeAlpha() {
    742             return mStrokeAlpha;
    743         }
    744 
    745         private void setStrokeAlpha(float strokeAlpha) {
    746             mStrokeAlpha = strokeAlpha;
    747         }
    748 
    749         private int getFillColor() {
    750             return mFillColor;
    751         }
    752 
    753         private void setFillColor(int fillColor) {
    754             mFillColor = fillColor;
    755         }
    756 
    757         private float getFillAlpha() {
    758             return mFillAlpha;
    759         }
    760 
    761         private void setFillAlpha(float fillAlpha) {
    762             mFillAlpha = fillAlpha;
    763         }
    764 
    765         private float getTrimPathStart() {
    766             return mTrimPathStart;
    767         }
    768 
    769         private void setTrimPathStart(float trimPathStart) {
    770             mTrimPathStart = trimPathStart;
    771         }
    772 
    773         private float getTrimPathEnd() {
    774             return mTrimPathEnd;
    775         }
    776 
    777         private void setTrimPathEnd(float trimPathEnd) {
    778             mTrimPathEnd = trimPathEnd;
    779         }
    780 
    781         private float getTrimPathOffset() {
    782             return mTrimPathOffset;
    783         }
    784 
    785         private void setTrimPathOffset(float trimPathOffset) {
    786             mTrimPathOffset = trimPathOffset;
    787         }
    788 
    789         private void setStrokeMiterlimit(float limit) {
    790             mStrokeMiterlimit = limit;
    791         }
    792 
    793         private float getStrokeMiterlimit() {
    794             return mStrokeMiterlimit;
    795         }
    796 
    797         private void setStrokeGradient(long gradientPtr) {
    798             mStrokeGradient = gradientPtr;
    799         }
    800 
    801         private void setFillGradient(long gradientPtr) {
    802             mFillGradient = gradientPtr;
    803         }
    804 
    805         private void setFillType(int fillType) {
    806             mFillType = fillType;
    807         }
    808 
    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 
    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 
    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 
    882         private VGroup_Delegate() {
    883         }
    884 
    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. */
    896         private float getRotation() {
    897             return mRotate;
    898         }
    899 
    900         private void setRotation(float rotation) {
    901             if (rotation != mRotate) {
    902                 mRotate = rotation;
    903                 updateLocalMatrix();
    904             }
    905         }
    906 
    907         private float getPivotX() {
    908             return mPivotX;
    909         }
    910 
    911         private void setPivotX(float pivotX) {
    912             if (pivotX != mPivotX) {
    913                 mPivotX = pivotX;
    914                 updateLocalMatrix();
    915             }
    916         }
    917 
    918         private float getPivotY() {
    919             return mPivotY;
    920         }
    921 
    922         private void setPivotY(float pivotY) {
    923             if (pivotY != mPivotY) {
    924                 mPivotY = pivotY;
    925                 updateLocalMatrix();
    926             }
    927         }
    928 
    929         private float getScaleX() {
    930             return mScaleX;
    931         }
    932 
    933         private void setScaleX(float scaleX) {
    934             if (scaleX != mScaleX) {
    935                 mScaleX = scaleX;
    936                 updateLocalMatrix();
    937             }
    938         }
    939 
    940         private float getScaleY() {
    941             return mScaleY;
    942         }
    943 
    944         private void setScaleY(float scaleY) {
    945             if (scaleY != mScaleY) {
    946                 mScaleY = scaleY;
    947                 updateLocalMatrix();
    948             }
    949         }
    950 
    951         private float getTranslateX() {
    952             return mTranslateX;
    953         }
    954 
    955         private void setTranslateX(float translateX) {
    956             if (translateX != mTranslateX) {
    957                 mTranslateX = translateX;
    958                 updateLocalMatrix();
    959             }
    960         }
    961 
    962         private float getTranslateY() {
    963             return mTranslateY;
    964         }
    965 
    966         private void setTranslateY(float translateY) {
    967             if (translateY != mTranslateY) {
    968                 mTranslateY = translateY;
    969                 updateLocalMatrix();
    970             }
    971         }
    972 
    973         @Override
    974         public void setName(String name) {
    975             mGroupName = name;
    976         }
    977 
    978         @Override
    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
    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 
   1003         public VPath_Delegate() {
   1004             // Empty constructor.
   1005         }
   1006 
   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 
   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
   1022         public void setName(String name) {
   1023             mPathName = name;
   1024         }
   1025 
   1026         public boolean isClipPath() {
   1027             return false;
   1028         }
   1029 
   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
   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 
   1072         private VPathRenderer_Delegate(long rootGroupPtr) {
   1073             mRootGroupPtr = rootGroupPtr;
   1074             mPath = new Path();
   1075             mRenderPath = new Path();
   1076         }
   1077 
   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 
   1086         private float getRootAlpha() {
   1087             return mRootAlpha;
   1088         }
   1089 
   1090         void setRootAlpha(float alpha) {
   1091             mRootAlpha = alpha;
   1092         }
   1093 
   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 
   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 
   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 
   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 
   1279         private void setAntiAlias(boolean aa) {
   1280             mAntiAlias = aa;
   1281         }
   1282 
   1283         @Override
   1284         public void setName(String name) {
   1285         }
   1286 
   1287         @Override
   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