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