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