3131import android .graphics .Paint .Style ;
3232import android .graphics .Path ;
3333import android .graphics .PixelFormat ;
34- import android .graphics .PointF ;
3534import android .graphics .PorterDuff ;
3635import android .graphics .PorterDuff .Mode ;
3736import android .graphics .PorterDuffColorFilter ;
5352import android .support .annotation .StyleRes ;
5453import com .google .android .material .internal .Experimental ;
5554import com .google .android .material .shadow .ShadowRenderer ;
55+ import com .google .android .material .shape .ShapeAppearancePathProvider .PathListener ;
5656import com .google .android .material .shape .ShapePath .ShadowCompatOperation ;
5757import android .support .v4 .graphics .drawable .TintAwareDrawable ;
5858import android .util .AttributeSet ;
@@ -97,9 +97,6 @@ public class MaterialShapeDrawable extends Drawable implements TintAwareDrawable
9797 private MaterialShapeDrawableState drawableState ;
9898
9999 // Inter-method state.
100- private final Matrix [] cornerTransforms = new Matrix [4 ];
101- private final Matrix [] edgeTransforms = new Matrix [4 ];
102- private final ShapePath [] cornerPaths = new ShapePath [4 ];
103100 private final ShadowCompatOperation [] cornerShadowOperation = new ShadowCompatOperation [4 ];
104101 private final ShadowCompatOperation [] edgeShadowOperation = new ShadowCompatOperation [4 ];
105102 private boolean pathDirty ;
@@ -108,20 +105,18 @@ public class MaterialShapeDrawable extends Drawable implements TintAwareDrawable
108105 private final Matrix matrix = new Matrix ();
109106 private final Path path = new Path ();
110107 private final Path pathInsetByStroke = new Path ();
111- private final PointF pointF = new PointF ();
112108 private final RectF rectF = new RectF ();
113109 private final RectF insetRectF = new RectF ();
114- private final ShapePath shapePath = new ShapePath ();
115110 private final Region transparentRegion = new Region ();
116111 private final Region scratchRegion = new Region ();
117- private final float [] scratch = new float [2 ];
118- private final float [] scratch2 = new float [2 ];
119112 private ShapeAppearanceModel strokeShapeAppearance ;
120113
121114 private final Paint fillPaint = new Paint (Paint .ANTI_ALIAS_FLAG );
122115 private final Paint strokePaint = new Paint (Paint .ANTI_ALIAS_FLAG );
123116
124117 private final ShadowRenderer shadowRenderer = new ShadowRenderer ();
118+ private final PathListener pathShadowListener ;
119+ private final ShapeAppearancePathProvider pathProvider = new ShapeAppearancePathProvider ();
125120
126121 @ Nullable private PorterDuffColorFilter tintFilter ;
127122 @ Nullable private PorterDuffColorFilter strokeTintFilter ;
@@ -149,13 +144,21 @@ private MaterialShapeDrawable(MaterialShapeDrawableState drawableState) {
149144 fillPaint .setStyle (Style .FILL );
150145 clearPaint .setColor (Color .WHITE );
151146 clearPaint .setXfermode (new PorterDuffXfermode (Mode .DST_OUT ));
152- for (int i = 0 ; i < 4 ; i ++) {
153- cornerTransforms [i ] = new Matrix ();
154- edgeTransforms [i ] = new Matrix ();
155- cornerPaths [i ] = new ShapePath ();
156- }
157147 updateTintFilter ();
158148 updateColorsForState (getState (), false );
149+ // Listens to additions of corners and edges, to create the shadow operations.
150+ pathShadowListener =
151+ new PathListener () {
152+ @ Override
153+ public void onCornerPathCreated (ShapePath cornerPath , Matrix transform , int count ) {
154+ cornerShadowOperation [count ] = cornerPath .createShadowCompatOperation (transform );
155+ }
156+
157+ @ Override
158+ public void onEdgePathCreated (ShapePath edgePath , Matrix transform , int count ) {
159+ edgeShadowOperation [count ] = edgePath .createShadowCompatOperation (transform );
160+ }
161+ };
159162 }
160163
161164 @ Nullable
@@ -846,10 +849,8 @@ private void drawCompatShadow(Canvas canvas) {
846849
847850 // Draw the fake shadow for each of the corners and edges.
848851 for (int index = 0 ; index < 4 ; index ++) {
849- cornerShadowOperation [index ].draw (
850- cornerTransforms [index ], shadowRenderer , drawableState .shadowCompatRadius , canvas );
851- edgeShadowOperation [index ].draw (
852- edgeTransforms [index ], shadowRenderer , drawableState .shadowCompatRadius , canvas );
852+ cornerShadowOperation [index ].draw (shadowRenderer , drawableState .shadowCompatRadius , canvas );
853+ edgeShadowOperation [index ].draw (shadowRenderer , drawableState .shadowCompatRadius , canvas );
853854 }
854855
855856 int shadowOffsetX =
@@ -871,34 +872,13 @@ public void getPathForSize(Rect bounds, Path path) {
871872 calculatePathForSize (new RectF (bounds ), path );
872873 }
873874
874- /**
875- * Writes to the given {@link Path} for the current edge and corner treatments at the specified
876- * size.
877- *
878- * @param bounds bounds of target path.
879- * @param path the returned path out-var.
880- * @return the generated path.
881- */
882875 private void calculatePathForSize (RectF bounds , Path path ) {
883- path .rewind ();
884-
885- // Calculate the transformations (rotations and translations) necessary for each edge and
886- // corner treatment.
887- for (int index = 0 ; index < 4 ; index ++) {
888- setCornerPathAndTransform (index , bounds );
889- setEdgePathAndTransform (index );
890- }
891-
892- // Apply corners and edges to the path in clockwise interleaving sequence: top-right corner,
893- // right edge, bottom-right corner, bottom edge, bottom-left corner etc. We start from the top
894- // right corner rather than the top left to work around a bug in API level 21 and 22 in which
895- // rounding error causes the path to incorrectly be marked as concave.
896- for (int index = 0 ; index < 4 ; index ++) {
897- appendCornerPath (index , path );
898- appendEdgePath (index , path );
899- }
900-
901- path .close ();
876+ pathProvider .calculatePath (
877+ drawableState .shapeAppearanceModel ,
878+ drawableState .interpolation ,
879+ bounds ,
880+ pathShadowListener ,
881+ path );
902882 }
903883
904884 /** Calculates the path that can be used to draw the stroke entirely inside the shape */
@@ -917,13 +897,11 @@ private void calculateStrokePath() {
917897 adjustCornerSizeForStrokeSize (cornerSizeBottomRight ),
918898 adjustCornerSizeForStrokeSize (cornerSizeBottomLeft ));
919899
920- //TODO: Clean up this logic when the ShapeAppearanceModel can calculate the path.
921- ShapeAppearanceModel tmp = drawableState .shapeAppearanceModel ;
922- drawableState .shapeAppearanceModel = strokeShapeAppearance ;
923-
924- calculatePath (getBoundsInsetByStroke (), pathInsetByStroke );
925-
926- drawableState .shapeAppearanceModel = tmp ;
900+ pathProvider .calculatePath (
901+ strokeShapeAppearance ,
902+ drawableState .interpolation ,
903+ getBoundsInsetByStroke (),
904+ pathInsetByStroke );
927905 }
928906
929907 private float adjustCornerSizeForStrokeSize (float cornerSize ) {
@@ -953,124 +931,6 @@ public void getOutline(Outline outline) {
953931 }
954932 }
955933
956- private void setCornerPathAndTransform (int index , RectF bounds ) {
957- getCornerTreatmentForIndex (index )
958- .getCornerPath (90 , drawableState .interpolation , cornerPaths [index ]);
959-
960- float edgeAngle = angleOfEdge (index );
961- cornerTransforms [index ].reset ();
962- getCoordinatesOfCorner (index , bounds , pointF );
963- cornerTransforms [index ].setTranslate (pointF .x , pointF .y );
964- cornerTransforms [index ].preRotate (edgeAngle );
965- }
966-
967- private void setEdgePathAndTransform (int index ) {
968- scratch [0 ] = cornerPaths [index ].endX ;
969- scratch [1 ] = cornerPaths [index ].endY ;
970- cornerTransforms [index ].mapPoints (scratch );
971- float edgeAngle = angleOfEdge (index );
972- edgeTransforms [index ].reset ();
973- edgeTransforms [index ].setTranslate (scratch [0 ], scratch [1 ]);
974- edgeTransforms [index ].preRotate (edgeAngle );
975- }
976-
977- private void appendCornerPath (int index , Path path ) {
978- scratch [0 ] = cornerPaths [index ].startX ;
979- scratch [1 ] = cornerPaths [index ].startY ;
980- cornerTransforms [index ].mapPoints (scratch );
981- if (index == 0 ) {
982- path .moveTo (scratch [0 ], scratch [1 ]);
983- } else {
984- path .lineTo (scratch [0 ], scratch [1 ]);
985- }
986- cornerPaths [index ].applyToPath (cornerTransforms [index ], path );
987- cornerShadowOperation [index ] = cornerPaths [index ].createShadowCompatOperation ();
988- }
989-
990- private void appendEdgePath (int index , Path path ) {
991- int nextIndex = (index + 1 ) % 4 ;
992- scratch [0 ] = cornerPaths [index ].endX ;
993- scratch [1 ] = cornerPaths [index ].endY ;
994- cornerTransforms [index ].mapPoints (scratch );
995-
996- scratch2 [0 ] = cornerPaths [nextIndex ].startX ;
997- scratch2 [1 ] = cornerPaths [nextIndex ].startY ;
998- cornerTransforms [nextIndex ].mapPoints (scratch2 );
999-
1000- float edgeLength = (float ) Math .hypot (scratch [0 ] - scratch2 [0 ], scratch [1 ] - scratch2 [1 ]);
1001- float center = getEdgeCenterForIndex (index );
1002- shapePath .reset (0 , 0 );
1003- getEdgeTreatmentForIndex (index )
1004- .getEdgePath (edgeLength , center , drawableState .interpolation , shapePath );
1005- shapePath .applyToPath (edgeTransforms [index ], path );
1006- edgeShadowOperation [index ] = shapePath .createShadowCompatOperation ();
1007- }
1008-
1009- private float getEdgeCenterForIndex (int index ) {
1010- scratch [0 ] = cornerPaths [index ].endX ;
1011- scratch [1 ] = cornerPaths [index ].endY ;
1012- cornerTransforms [index ].mapPoints (scratch );
1013- switch (index ) {
1014- case 1 :
1015- case 3 :
1016- return Math .abs (getBoundsAsRectF ().centerX () - scratch [0 ]);
1017- case 2 :
1018- case 0 :
1019- default :
1020- return Math .abs (getBoundsAsRectF ().centerY () - scratch [1 ]);
1021- }
1022- }
1023-
1024- private CornerTreatment getCornerTreatmentForIndex (int index ) {
1025- switch (index ) {
1026- case 1 :
1027- return drawableState .shapeAppearanceModel .getBottomRightCorner ();
1028- case 2 :
1029- return drawableState .shapeAppearanceModel .getBottomLeftCorner ();
1030- case 3 :
1031- return drawableState .shapeAppearanceModel .getTopLeftCorner ();
1032- case 0 :
1033- default :
1034- return drawableState .shapeAppearanceModel .getTopRightCorner ();
1035- }
1036- }
1037-
1038- private EdgeTreatment getEdgeTreatmentForIndex (int index ) {
1039- switch (index ) {
1040- case 1 :
1041- return drawableState .shapeAppearanceModel .getBottomEdge ();
1042- case 2 :
1043- return drawableState .shapeAppearanceModel .getLeftEdge ();
1044- case 3 :
1045- return drawableState .shapeAppearanceModel .getTopEdge ();
1046- case 0 :
1047- default :
1048- return drawableState .shapeAppearanceModel .getRightEdge ();
1049- }
1050- }
1051-
1052- private void getCoordinatesOfCorner (int index , RectF bounds , PointF pointF ) {
1053- switch (index ) {
1054- case 1 : // bottom-right
1055- pointF .set (bounds .right , bounds .bottom );
1056- break ;
1057- case 2 : // bottom-left
1058- pointF .set (bounds .left , bounds .bottom );
1059- break ;
1060- case 3 : // top-left
1061- pointF .set (bounds .left , bounds .top );
1062- break ;
1063- case 0 : // top-right
1064- default :
1065- pointF .set (bounds .right , bounds .top );
1066- break ;
1067- }
1068- }
1069-
1070- private float angleOfEdge (int index ) {
1071- return 90 * (index + 1 % 4 );
1072- }
1073-
1074934 private void calculatePath (RectF bounds , Path path ) {
1075935 calculatePathForSize (bounds , path );
1076936 if (drawableState .scale == 1f ) {
0 commit comments