Deepanshu Gupta | 00c2adf | 2015-02-03 19:02:15 -0800 | [diff] [blame] | 1 | /* |
Charlie Tsai | e18bf49 | 2017-03-02 18:39:45 +0000 | [diff] [blame] | 2 | * Copyright (C) 2015, 2017 The Android Open Source Project |
Deepanshu Gupta | 00c2adf | 2015-02-03 19:02:15 -0800 | [diff] [blame] | 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.view; |
| 18 | |
Charlie Tsai | e18bf49 | 2017-03-02 18:39:45 +0000 | [diff] [blame] | 19 | import android.annotation.NonNull; |
Deepanshu Gupta | 00c2adf | 2015-02-03 19:02:15 -0800 | [diff] [blame] | 20 | import android.graphics.Canvas; |
Deepanshu Gupta | 00c2adf | 2015-02-03 19:02:15 -0800 | [diff] [blame] | 21 | import android.graphics.Outline; |
Deepanshu Gupta | 00c2adf | 2015-02-03 19:02:15 -0800 | [diff] [blame] | 22 | import android.graphics.Rect; |
Charlie Tsai | e18bf49 | 2017-03-02 18:39:45 +0000 | [diff] [blame] | 23 | import com.android.layoutlib.bridge.shadowutil.SpotShadow; |
| 24 | import com.android.layoutlib.bridge.shadowutil.ShadowBuffer; |
Deepanshu Gupta | 00c2adf | 2015-02-03 19:02:15 -0800 | [diff] [blame] | 25 | |
Deepanshu Gupta | 00c2adf | 2015-02-03 19:02:15 -0800 | [diff] [blame] | 26 | public class RectShadowPainter { |
| 27 | |
Charlie Tsai | e18bf49 | 2017-03-02 18:39:45 +0000 | [diff] [blame] | 28 | private static final float SHADOW_STRENGTH = 0.1f; |
| 29 | private static final int LIGHT_POINTS = 8; |
Deepanshu Gupta | 00c2adf | 2015-02-03 19:02:15 -0800 | [diff] [blame] | 30 | |
Charlie Tsai | e18bf49 | 2017-03-02 18:39:45 +0000 | [diff] [blame] | 31 | private static final int QUADRANT_DIVIDED_COUNT = 8; |
Deepanshu Gupta | 00c2adf | 2015-02-03 19:02:15 -0800 | [diff] [blame] | 32 | |
Charlie Tsai | e18bf49 | 2017-03-02 18:39:45 +0000 | [diff] [blame] | 33 | private static final int RAY_TRACING_RAYS = 180; |
| 34 | private static final int RAY_TRACING_LAYERS = 10; |
| 35 | |
| 36 | public static void paintShadow(@NonNull Outline viewOutline, float elevation, |
| 37 | @NonNull Canvas canvas) { |
Diego Perez | a4d7ad8 | 2016-04-06 12:17:34 +0100 | [diff] [blame] | 38 | Rect outline = new Rect(); |
| 39 | if (!viewOutline.getRect(outline)) { |
Diego Perez | d88c717 | 2017-03-29 17:20:01 +0100 | [diff] [blame] | 40 | assert false : "Outline is not a rect shadow"; |
| 41 | return; |
Diego Perez | a4d7ad8 | 2016-04-06 12:17:34 +0100 | [diff] [blame] | 42 | } |
| 43 | |
Charlie Tsai | e18bf49 | 2017-03-02 18:39:45 +0000 | [diff] [blame] | 44 | Rect originCanvasRect = canvas.getClipBounds(); |
| 45 | int saved = modifyCanvas(canvas); |
Deepanshu Gupta | 00c2adf | 2015-02-03 19:02:15 -0800 | [diff] [blame] | 46 | if (saved == -1) { |
| 47 | return; |
| 48 | } |
| 49 | try { |
Charlie Tsai | e18bf49 | 2017-03-02 18:39:45 +0000 | [diff] [blame] | 50 | float radius = viewOutline.getRadius(); |
| 51 | if (radius <= 0) { |
| 52 | // We can not paint a shadow with radius 0 |
| 53 | return; |
| 54 | } |
Deepanshu Gupta | 00c2adf | 2015-02-03 19:02:15 -0800 | [diff] [blame] | 55 | |
Charlie Tsai | e18bf49 | 2017-03-02 18:39:45 +0000 | [diff] [blame] | 56 | // view's absolute position in this canvas. |
| 57 | int viewLeft = -originCanvasRect.left + outline.left; |
| 58 | int viewTop = -originCanvasRect.top + outline.top; |
| 59 | int viewRight = viewLeft + outline.width(); |
| 60 | int viewBottom = viewTop + outline.height(); |
Deepanshu Gupta | 00c2adf | 2015-02-03 19:02:15 -0800 | [diff] [blame] | 61 | |
Charlie Tsai | e18bf49 | 2017-03-02 18:39:45 +0000 | [diff] [blame] | 62 | float[][] rectangleCoordinators = generateRectangleCoordinates(viewLeft, viewTop, |
| 63 | viewRight, viewBottom, radius, elevation); |
Deepanshu Gupta | 00c2adf | 2015-02-03 19:02:15 -0800 | [diff] [blame] | 64 | |
Charlie Tsai | e18bf49 | 2017-03-02 18:39:45 +0000 | [diff] [blame] | 65 | // TODO: get these values from resources. |
| 66 | float lightPosX = canvas.getWidth() / 2; |
| 67 | float lightPosY = 0; |
| 68 | float lightHeight = 1800; |
| 69 | float lightSize = 200; |
| 70 | |
| 71 | paintGeometricShadow(rectangleCoordinators, lightPosX, lightPosY, lightHeight, |
| 72 | lightSize, canvas); |
Deepanshu Gupta | 00c2adf | 2015-02-03 19:02:15 -0800 | [diff] [blame] | 73 | } finally { |
| 74 | canvas.restoreToCount(saved); |
| 75 | } |
| 76 | } |
| 77 | |
Charlie Tsai | e18bf49 | 2017-03-02 18:39:45 +0000 | [diff] [blame] | 78 | private static int modifyCanvas(@NonNull Canvas canvas) { |
| 79 | Rect rect = canvas.getClipBounds(); |
| 80 | canvas.translate(rect.left, rect.top); |
| 81 | return canvas.save(); |
Deepanshu Gupta | 00c2adf | 2015-02-03 19:02:15 -0800 | [diff] [blame] | 82 | } |
| 83 | |
Charlie Tsai | e18bf49 | 2017-03-02 18:39:45 +0000 | [diff] [blame] | 84 | @NonNull |
| 85 | private static float[][] generateRectangleCoordinates(float left, float top, float right, |
| 86 | float bottom, float radius, float elevation) { |
| 87 | left = left + radius; |
| 88 | top = top + radius; |
| 89 | right = right - radius; |
| 90 | bottom = bottom - radius; |
| 91 | |
| 92 | final double RADIANS_STEP = 2 * Math.PI / 4 / QUADRANT_DIVIDED_COUNT; |
| 93 | |
| 94 | float[][] ret = new float[QUADRANT_DIVIDED_COUNT * 4][3]; |
| 95 | |
| 96 | int points = 0; |
| 97 | // left-bottom points |
| 98 | for (int i = 0; i < QUADRANT_DIVIDED_COUNT; i++) { |
| 99 | ret[points][0] = (float) (left - radius + radius * Math.cos(RADIANS_STEP * i)); |
| 100 | ret[points][1] = (float) (bottom + radius - radius * Math.cos(RADIANS_STEP * i)); |
| 101 | ret[points][2] = elevation; |
| 102 | points++; |
Deepanshu Gupta | 00c2adf | 2015-02-03 19:02:15 -0800 | [diff] [blame] | 103 | } |
Charlie Tsai | e18bf49 | 2017-03-02 18:39:45 +0000 | [diff] [blame] | 104 | // left-top points |
| 105 | for (int i = 0; i < QUADRANT_DIVIDED_COUNT; i++) { |
| 106 | ret[points][0] = (float) (left + radius - radius * Math.cos(RADIANS_STEP * i)); |
| 107 | ret[points][1] = (float) (top + radius - radius * Math.cos(RADIANS_STEP * i)); |
| 108 | ret[points][2] = elevation; |
| 109 | points++; |
Jerome Gaillard | 555fcd5 | 2016-08-12 15:43:48 -0700 | [diff] [blame] | 110 | } |
Charlie Tsai | e18bf49 | 2017-03-02 18:39:45 +0000 | [diff] [blame] | 111 | // right-top points |
| 112 | for (int i = 0; i < QUADRANT_DIVIDED_COUNT; i++) { |
| 113 | ret[points][0] = (float) (right + radius - radius * Math.cos(RADIANS_STEP * i)); |
| 114 | ret[points][1] = (float) (top + radius + radius * Math.cos(RADIANS_STEP * i)); |
| 115 | ret[points][2] = elevation; |
| 116 | points++; |
| 117 | } |
| 118 | // right-bottom point |
| 119 | for (int i = 0; i < QUADRANT_DIVIDED_COUNT; i++) { |
| 120 | ret[points][0] = (float) (right - radius + radius * Math.cos(RADIANS_STEP * i)); |
| 121 | ret[points][1] = (float) (bottom - radius + radius * Math.cos(RADIANS_STEP * i)); |
| 122 | ret[points][2] = elevation; |
| 123 | points++; |
| 124 | } |
| 125 | |
| 126 | return ret; |
Deepanshu Gupta | 00c2adf | 2015-02-03 19:02:15 -0800 | [diff] [blame] | 127 | } |
| 128 | |
Charlie Tsai | e18bf49 | 2017-03-02 18:39:45 +0000 | [diff] [blame] | 129 | private static void paintGeometricShadow(@NonNull float[][] coordinates, float lightPosX, |
| 130 | float lightPosY, float lightHeight, float lightSize, Canvas canvas) { |
Diego Perez | ef74ede | 2017-03-24 17:24:57 +0000 | [diff] [blame] | 131 | if (canvas == null || canvas.getWidth() == 0 || canvas.getHeight() == 0) { |
| 132 | return; |
| 133 | } |
Charlie Tsai | e18bf49 | 2017-03-02 18:39:45 +0000 | [diff] [blame] | 134 | |
| 135 | // The polygon of shadow (same as the original item) |
| 136 | float[] shadowPoly = new float[coordinates.length * 3]; |
| 137 | for (int i = 0; i < coordinates.length; i++) { |
| 138 | shadowPoly[i * 3 + 0] = coordinates[i][0]; |
| 139 | shadowPoly[i * 3 + 1] = coordinates[i][1]; |
| 140 | shadowPoly[i * 3 + 2] = coordinates[i][2]; |
| 141 | } |
| 142 | |
| 143 | // TODO: calculate the ambient shadow and mix with Spot shadow. |
| 144 | |
| 145 | // Calculate the shadow of SpotLight |
| 146 | float[] light = SpotShadow.calculateLight(lightSize, LIGHT_POINTS, lightPosX, |
| 147 | lightPosY, lightHeight); |
| 148 | |
| 149 | int stripSize = 3 * SpotShadow.getStripSize(RAY_TRACING_RAYS, RAY_TRACING_LAYERS); |
| 150 | if (stripSize < 9) { |
Deepanshu Gupta | 0aa004c | 2015-04-29 10:44:51 -0700 | [diff] [blame] | 151 | return; |
| 152 | } |
Charlie Tsai | e18bf49 | 2017-03-02 18:39:45 +0000 | [diff] [blame] | 153 | float[] strip = new float[stripSize]; |
| 154 | SpotShadow.calcShadow(light, LIGHT_POINTS, shadowPoly, coordinates.length, RAY_TRACING_RAYS, |
| 155 | RAY_TRACING_LAYERS, 1f, strip); |
Deepanshu Gupta | 00c2adf | 2015-02-03 19:02:15 -0800 | [diff] [blame] | 156 | |
Charlie Tsai | e18bf49 | 2017-03-02 18:39:45 +0000 | [diff] [blame] | 157 | ShadowBuffer buff = new ShadowBuffer(canvas.getWidth(), canvas.getHeight()); |
| 158 | buff.generateTriangles(strip, SHADOW_STRENGTH); |
| 159 | buff.draw(canvas); |
Deepanshu Gupta | 0aa004c | 2015-04-29 10:44:51 -0700 | [diff] [blame] | 160 | } |
Deepanshu Gupta | 00c2adf | 2015-02-03 19:02:15 -0800 | [diff] [blame] | 161 | } |