diff options
author | 2017-05-05 11:51:47 +0100 | |
---|---|---|
committer | 2017-05-06 19:10:14 +0100 | |
commit | 8618589908e6df711c75dc91b9e2bfeeb87f3d00 (patch) | |
tree | abdee26392e18e6cc0451402602ac5eef9a405e3 | |
parent | 47e090c391ab5b21ec8d55f0798914413dd0ff02 (diff) |
Draw rectangle shadow fast in low elevation cases
When the elevation is not high, the shadow is similar
to just add some dark lines on the edges and corners of
rectangle views. Using simple algorithm can also improve
the performance and save more memory.
Test: The render result of unit test is updated.
Bug: 37906145
Change-Id: I28758f868ee6e24e4552368bddfb7ac10fe0a205
-rw-r--r-- | tools/layoutlib/bridge/src/android/view/RectShadowPainter.java | 138 | ||||
-rw-r--r-- | tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/shadows_test.png | bin | 13569 -> 11423 bytes |
2 files changed, 124 insertions, 14 deletions
diff --git a/tools/layoutlib/bridge/src/android/view/RectShadowPainter.java b/tools/layoutlib/bridge/src/android/view/RectShadowPainter.java index 8ae212cd2ebf..a5626e09b795 100644 --- a/tools/layoutlib/bridge/src/android/view/RectShadowPainter.java +++ b/tools/layoutlib/bridge/src/android/view/RectShadowPainter.java @@ -18,13 +18,28 @@ package android.view; import android.annotation.NonNull; import android.graphics.Canvas; +import android.graphics.LinearGradient; import android.graphics.Outline; +import android.graphics.Paint; +import android.graphics.Paint.Style; +import android.graphics.Path; +import android.graphics.Path.FillType; +import android.graphics.RadialGradient; import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.Shader.TileMode; + +import com.android.layoutlib.bridge.impl.ResourceHelper; import com.android.layoutlib.bridge.shadowutil.SpotShadow; import com.android.layoutlib.bridge.shadowutil.ShadowBuffer; public class RectShadowPainter { + private static final float SIMPLE_SHADOW_ELEVATION_THRESHOLD = 16f; + private static final int SIMPLE_SHADOW_START_COLOR = ResourceHelper.getColor("#37000000"); + private static final int SIMPLE_SHADOW_END_COLOR = ResourceHelper.getColor("#03000000"); + private static final float PERPENDICULAR_ANGLE = 90f; + private static final float SHADOW_STRENGTH = 0.1f; private static final int LIGHT_POINTS = 8; @@ -53,23 +68,30 @@ public class RectShadowPainter { return; } - // view's absolute position in this canvas. - int viewLeft = -originCanvasRect.left + outline.left; - int viewTop = -originCanvasRect.top + outline.top; - int viewRight = viewLeft + outline.width(); - int viewBottom = viewTop + outline.height(); + if (elevation <= SIMPLE_SHADOW_ELEVATION_THRESHOLD) { + // When elevation is not high, the shadow is very similar to a small outline of + // View. For the performance reason, we draw the shadow in simple way. + simpleRectangleShadow(canvas, outline, radius, elevation); + } else { + // view's absolute position in this canvas. + int viewLeft = -originCanvasRect.left + outline.left; + int viewTop = -originCanvasRect.top + outline.top; + int viewRight = viewLeft + outline.width(); + int viewBottom = viewTop + outline.height(); - float[][] rectangleCoordinators = generateRectangleCoordinates(viewLeft, viewTop, - viewRight, viewBottom, radius, elevation); + float[][] rectangleCoordinators = + generateRectangleCoordinates(viewLeft, viewTop, viewRight, viewBottom, + radius, elevation); - // TODO: get these values from resources. - float lightPosX = canvas.getWidth() / 2; - float lightPosY = 0; - float lightHeight = 1800; - float lightSize = 200; + // TODO: get these values from resources. + float lightPosX = canvas.getWidth() / 2; + float lightPosY = 0; + float lightHeight = 1800; + float lightSize = 200; - paintGeometricShadow(rectangleCoordinators, lightPosX, lightPosY, lightHeight, - lightSize, canvas); + paintGeometricShadow(rectangleCoordinators, lightPosX, lightPosY, lightHeight, + lightSize, canvas); + } } finally { canvas.restoreToCount(saved); } @@ -81,6 +103,94 @@ public class RectShadowPainter { return canvas.save(); } + private static void simpleRectangleShadow(@NonNull Canvas canvas, @NonNull Rect outline, + float radius, float elevation) { + float shadowSize = elevation / 2; + + Paint cornerPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG); + cornerPaint.setStyle(Style.FILL); + Paint edgePaint = new Paint(cornerPaint); + edgePaint.setAntiAlias(false); + float outerArcRadius = radius + shadowSize; + int[] colors = {SIMPLE_SHADOW_START_COLOR, SIMPLE_SHADOW_START_COLOR, + SIMPLE_SHADOW_END_COLOR}; + cornerPaint.setShader(new RadialGradient(0, 0, outerArcRadius, colors, + new float[]{0f, radius / outerArcRadius, 1f}, TileMode.CLAMP)); + edgePaint.setShader(new LinearGradient(0, 0, -shadowSize, 0, SIMPLE_SHADOW_START_COLOR, + SIMPLE_SHADOW_END_COLOR, + TileMode.CLAMP)); + Path path = new Path(); + path.setFillType(FillType.EVEN_ODD); + // A rectangle bounding the complete shadow. + RectF shadowRect = new RectF(outline); + shadowRect.inset(-shadowSize, -shadowSize); + // A rectangle with edges corresponding to the straight edges of the outline. + RectF inset = new RectF(outline); + inset.inset(radius, radius); + // A rectangle used to represent the edge shadow. + RectF edgeShadowRect = new RectF(); + + + // left and right sides. + edgeShadowRect.set(-shadowSize, 0f, 0f, inset.height()); + // Left shadow + drawSideShadow(canvas, edgePaint, edgeShadowRect, outline.left, inset.top, 0); + // Right shadow + drawSideShadow(canvas, edgePaint, edgeShadowRect, outline.right, inset.bottom, 2); + // Top shadow + edgeShadowRect.set(-shadowSize, 0, 0, inset.width()); + drawSideShadow(canvas, edgePaint, edgeShadowRect, inset.right, outline.top, 1); + // bottom shadow. This needs an inset so that blank doesn't appear when the content is + // moved up. + edgeShadowRect.set(-shadowSize, 0, shadowSize / 2f, inset.width()); + edgePaint.setShader( + new LinearGradient(edgeShadowRect.right, 0, edgeShadowRect.left, 0, colors, + new float[]{0f, 1 / 3f, 1f}, TileMode.CLAMP)); + drawSideShadow(canvas, edgePaint, edgeShadowRect, inset.left, outline.bottom, 3); + + // Draw corners. + drawCorner(canvas, cornerPaint, path, inset.right, inset.bottom, outerArcRadius, 0); + drawCorner(canvas, cornerPaint, path, inset.left, inset.bottom, outerArcRadius, 1); + drawCorner(canvas, cornerPaint, path, inset.left, inset.top, outerArcRadius, 2); + drawCorner(canvas, cornerPaint, path, inset.right, inset.top, outerArcRadius, 3); + } + + private static void drawSideShadow(@NonNull Canvas canvas, @NonNull Paint edgePaint, + @NonNull RectF shadowRect, float dx, float dy, int rotations) { + if ((int) shadowRect.left >= (int) shadowRect.right || + (int) shadowRect.top >= (int) shadowRect.bottom) { + // Rect is empty, no need to draw shadow + return; + } + int saved = canvas.save(); + canvas.translate(dx, dy); + canvas.rotate(rotations * PERPENDICULAR_ANGLE); + canvas.drawRect(shadowRect, edgePaint); + canvas.restoreToCount(saved); + } + + /** + * @param canvas Canvas to draw the rectangle on. + * @param paint Paint to use when drawing the corner. + * @param path A path to reuse. Prevents allocating memory for each path. + * @param x Center of circle, which this corner is a part of. + * @param y Center of circle, which this corner is a part of. + * @param radius radius of the arc + * @param rotations number of quarter rotations before starting to paint the arc. + */ + private static void drawCorner(@NonNull Canvas canvas, @NonNull Paint paint, @NonNull Path path, + float x, float y, float radius, int rotations) { + int saved = canvas.save(); + canvas.translate(x, y); + path.reset(); + path.arcTo(-radius, -radius, radius, radius, rotations * PERPENDICULAR_ANGLE, + PERPENDICULAR_ANGLE, false); + path.lineTo(0, 0); + path.close(); + canvas.drawPath(path, paint); + canvas.restoreToCount(saved); + } + @NonNull private static float[][] generateRectangleCoordinates(float left, float top, float right, float bottom, float radius, float elevation) { diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/shadows_test.png b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/shadows_test.png Binary files differindex 67355b821345..4951629a50b6 100644 --- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/shadows_test.png +++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/shadows_test.png |