blob: 8ae212cd2ebfe7752d1a568d533880b33794ed21 [file] [log] [blame]
Deepanshu Gupta00c2adf2015-02-03 19:02:15 -08001/*
Charlie Tsaie18bf492017-03-02 18:39:45 +00002 * Copyright (C) 2015, 2017 The Android Open Source Project
Deepanshu Gupta00c2adf2015-02-03 19:02:15 -08003 *
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
17package android.view;
18
Charlie Tsaie18bf492017-03-02 18:39:45 +000019import android.annotation.NonNull;
Deepanshu Gupta00c2adf2015-02-03 19:02:15 -080020import android.graphics.Canvas;
Deepanshu Gupta00c2adf2015-02-03 19:02:15 -080021import android.graphics.Outline;
Deepanshu Gupta00c2adf2015-02-03 19:02:15 -080022import android.graphics.Rect;
Charlie Tsaie18bf492017-03-02 18:39:45 +000023import com.android.layoutlib.bridge.shadowutil.SpotShadow;
24import com.android.layoutlib.bridge.shadowutil.ShadowBuffer;
Deepanshu Gupta00c2adf2015-02-03 19:02:15 -080025
Deepanshu Gupta00c2adf2015-02-03 19:02:15 -080026public class RectShadowPainter {
27
Charlie Tsaie18bf492017-03-02 18:39:45 +000028 private static final float SHADOW_STRENGTH = 0.1f;
29 private static final int LIGHT_POINTS = 8;
Deepanshu Gupta00c2adf2015-02-03 19:02:15 -080030
Charlie Tsaie18bf492017-03-02 18:39:45 +000031 private static final int QUADRANT_DIVIDED_COUNT = 8;
Deepanshu Gupta00c2adf2015-02-03 19:02:15 -080032
Charlie Tsaie18bf492017-03-02 18:39:45 +000033 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 Pereza4d7ad82016-04-06 12:17:34 +010038 Rect outline = new Rect();
39 if (!viewOutline.getRect(outline)) {
Diego Perezd88c7172017-03-29 17:20:01 +010040 assert false : "Outline is not a rect shadow";
41 return;
Diego Pereza4d7ad82016-04-06 12:17:34 +010042 }
43
Charlie Tsaie18bf492017-03-02 18:39:45 +000044 Rect originCanvasRect = canvas.getClipBounds();
45 int saved = modifyCanvas(canvas);
Deepanshu Gupta00c2adf2015-02-03 19:02:15 -080046 if (saved == -1) {
47 return;
48 }
49 try {
Charlie Tsaie18bf492017-03-02 18:39:45 +000050 float radius = viewOutline.getRadius();
51 if (radius <= 0) {
52 // We can not paint a shadow with radius 0
53 return;
54 }
Deepanshu Gupta00c2adf2015-02-03 19:02:15 -080055
Charlie Tsaie18bf492017-03-02 18:39:45 +000056 // 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 Gupta00c2adf2015-02-03 19:02:15 -080061
Charlie Tsaie18bf492017-03-02 18:39:45 +000062 float[][] rectangleCoordinators = generateRectangleCoordinates(viewLeft, viewTop,
63 viewRight, viewBottom, radius, elevation);
Deepanshu Gupta00c2adf2015-02-03 19:02:15 -080064
Charlie Tsaie18bf492017-03-02 18:39:45 +000065 // 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 Gupta00c2adf2015-02-03 19:02:15 -080073 } finally {
74 canvas.restoreToCount(saved);
75 }
76 }
77
Charlie Tsaie18bf492017-03-02 18:39:45 +000078 private static int modifyCanvas(@NonNull Canvas canvas) {
79 Rect rect = canvas.getClipBounds();
80 canvas.translate(rect.left, rect.top);
81 return canvas.save();
Deepanshu Gupta00c2adf2015-02-03 19:02:15 -080082 }
83
Charlie Tsaie18bf492017-03-02 18:39:45 +000084 @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 Gupta00c2adf2015-02-03 19:02:15 -0800103 }
Charlie Tsaie18bf492017-03-02 18:39:45 +0000104 // 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 Gaillard555fcd52016-08-12 15:43:48 -0700110 }
Charlie Tsaie18bf492017-03-02 18:39:45 +0000111 // 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 Gupta00c2adf2015-02-03 19:02:15 -0800127 }
128
Charlie Tsaie18bf492017-03-02 18:39:45 +0000129 private static void paintGeometricShadow(@NonNull float[][] coordinates, float lightPosX,
130 float lightPosY, float lightHeight, float lightSize, Canvas canvas) {
Diego Perezef74ede2017-03-24 17:24:57 +0000131 if (canvas == null || canvas.getWidth() == 0 || canvas.getHeight() == 0) {
132 return;
133 }
Charlie Tsaie18bf492017-03-02 18:39:45 +0000134
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 Gupta0aa004c2015-04-29 10:44:51 -0700151 return;
152 }
Charlie Tsaie18bf492017-03-02 18:39:45 +0000153 float[] strip = new float[stripSize];
154 SpotShadow.calcShadow(light, LIGHT_POINTS, shadowPoly, coordinates.length, RAY_TRACING_RAYS,
155 RAY_TRACING_LAYERS, 1f, strip);
Deepanshu Gupta00c2adf2015-02-03 19:02:15 -0800156
Charlie Tsaie18bf492017-03-02 18:39:45 +0000157 ShadowBuffer buff = new ShadowBuffer(canvas.getWidth(), canvas.getHeight());
158 buff.generateTriangles(strip, SHADOW_STRENGTH);
159 buff.draw(canvas);
Deepanshu Gupta0aa004c2015-04-29 10:44:51 -0700160 }
Deepanshu Gupta00c2adf2015-02-03 19:02:15 -0800161}