• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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.view.shadow;
18 
19 import com.android.ide.common.rendering.api.LayoutLog;
20 import com.android.layoutlib.bridge.Bridge;
21 import com.android.tools.layoutlib.annotations.VisibleForTesting;
22 
23 import android.graphics.Bitmap;
24 import android.view.math.Math3DHelper;
25 
26 /**
27  * Generate spot shadow bitmap.
28  */
29 class SpotShadowBitmapGenerator {
30 
31     private final SpotShadowConfig mShadowConfig;
32     private final TriangleBuffer mTriangle;
33     private float[] mStrips;
34     private float[] mLightSources;
35     private float mTranslateX;
36     private float mTranslateY;
37 
SpotShadowBitmapGenerator(SpotShadowConfig config)38     public SpotShadowBitmapGenerator(SpotShadowConfig config) {
39         // TODO: Reduce the buffer size based on shadow bounds.
40         mTriangle = new TriangleBuffer();
41         mShadowConfig = config;
42         // For now assume no change to the world size
43         mTriangle.setSize(config.getWidth(), config.getHeight(), 0);
44     }
45 
46     /**
47      * Populate the shadow bitmap.
48      */
populateShadow()49     public void populateShadow() {
50         try {
51             mLightSources = SpotShadowVertexCalculator.calculateLight(
52                     mShadowConfig.getLightRadius(),
53                     mShadowConfig.getLightSourcePoints(),
54                     mShadowConfig.getLightCoord()[0],
55                     mShadowConfig.getLightCoord()[1],
56                     mShadowConfig.getLightCoord()[2]);
57 
58             mStrips = new float[3 * SpotShadowVertexCalculator.getStripSize(
59                     mShadowConfig.getRays(),
60                     mShadowConfig.getLayers())];
61 
62             if (SpotShadowVertexCalculator.calculateShadow(
63                     mLightSources,
64                     mShadowConfig.getLightSourcePoints(),
65                     mShadowConfig.getPoly(),
66                     mShadowConfig.getPolyLength(),
67                     mShadowConfig.getRays(),
68                     mShadowConfig.getLayers(),
69                     mShadowConfig.getShadowStrength(),
70                     mStrips) != 1) {
71                 return;
72             }
73 
74             // Bit of a hack to re-adjust spot shadow to fit correctly within parent canvas.
75             // Problem is that outline passed is not a final position, which throws off our
76             // whereas our shadow rendering algorithm, which requires pre-set range for
77             // optimization purposes.
78             float[] shadowBounds = Math3DHelper.flatBound(mStrips, 3);
79 
80             if ((shadowBounds[2] - shadowBounds[0]) > mShadowConfig.getWidth() ||
81                     (shadowBounds[3] - shadowBounds[1]) > mShadowConfig.getHeight()) {
82                 // Spot shadow to be casted is larger than the parent canvas,
83                 // We'll let ambient shadow do the trick and skip spot shadow here.
84                 return;
85             }
86 
87             mTranslateX = 0;
88             mTranslateY = 0;
89             if (shadowBounds[0] < 0) {
90                 // translate to right by the offset amount.
91                 mTranslateX = shadowBounds[0] * -1;
92             } else if (shadowBounds[2] > mShadowConfig.getWidth()) {
93                 // translate to left by the offset amount.
94                 mTranslateX = shadowBounds[2] - mShadowConfig.getWidth();
95             }
96 
97             if (shadowBounds[1] < 0) {
98                 mTranslateY = shadowBounds[1] * -1;
99             } else if (shadowBounds[3] > mShadowConfig.getHeight()) {
100                 mTranslateY = shadowBounds[3] - mShadowConfig.getHeight();
101             }
102             Math3DHelper.translate(mStrips, mTranslateX, mTranslateY, 3);
103 
104             mTriangle.drawTriangles(mStrips, mShadowConfig.getShadowStrength());
105         } catch (IndexOutOfBoundsException|ArithmeticException mathError) {
106             Bridge.getLog().warning(LayoutLog.TAG_INFO,  "Arithmetic error while drawing " +
107                             "spot shadow",
108                     mathError);
109         } catch (Exception ex) {
110             Bridge.getLog().warning(LayoutLog.TAG_INFO,  "Error while drawing shadow",
111                     ex);
112         }
113     }
114 
getTranslateX()115     public float getTranslateX() {
116         return mTranslateX;
117     }
118 
getTranslateY()119     public float getTranslateY() {
120         return mTranslateY;
121     }
122 
clear()123     public void clear() {
124         mTriangle.clear();
125     }
126 
127     /**
128      * @return true if generated shadow poly is valid. False otherwise.
129      */
validate()130     public boolean validate() {
131         return mStrips != null && mStrips.length >= 9;
132     }
133 
134     /**
135      * @return the bitmap of shadow after it's populated
136      */
getBitmap()137     public Bitmap getBitmap() {
138         return mTriangle.getImage();
139     }
140 
141     @VisibleForTesting
getStrips()142     public float[] getStrips() {
143         return mStrips;
144     }
145 
146     @VisibleForTesting
updateLightSource(float x, float y)147     public void updateLightSource(float x, float y) {
148         mShadowConfig.setLightCoord(x, y);
149     }
150 }
151