1 /*
2  * Copyright 2019 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 #include <math/vec4.h>
18 
19 #include <renderengine/Mesh.h>
20 
21 #include <ui/Rect.h>
22 #include <ui/Transform.h>
23 
24 #include <utils/Log.h>
25 
26 #include "GLSkiaShadowPort.h"
27 
28 namespace android {
29 namespace renderengine {
30 namespace gl {
31 
32 /**
33  * The shadow geometry logic and vertex generation code has been ported from skia shadow
34  * fast path OpenGL implementation to draw shadows around rects and rounded rects including
35  * circles.
36  *
37  * path: skia/src/gpu/GrRenderTargetContext.cpp GrRenderTargetContext::drawFastShadow
38  *
39  * Modifications made:
40  * - Switched to using std lib math functions
41  * - Fall off function is implemented in vertex shader rather than a shadow texture
42  * - Removed transformations applied on the caster rect since the caster will be in local
43  *   coordinate space and will be transformed by the vertex shader.
44  */
45 
divide_and_pin(float numer,float denom,float min,float max)46 static inline float divide_and_pin(float numer, float denom, float min, float max) {
47     if (denom == 0.0f) return min;
48     return std::clamp(numer / denom, min, max);
49 }
50 
51 static constexpr auto SK_ScalarSqrt2 = 1.41421356f;
52 static constexpr auto kAmbientHeightFactor = 1.0f / 128.0f;
53 static constexpr auto kAmbientGeomFactor = 64.0f;
54 // Assuming that we have a light height of 600 for the spot shadow,
55 // the spot values will reach their maximum at a height of approximately 292.3077.
56 // We'll round up to 300 to keep it simple.
57 static constexpr auto kMaxAmbientRadius = 300 * kAmbientHeightFactor * kAmbientGeomFactor;
58 
AmbientBlurRadius(float height)59 inline float AmbientBlurRadius(float height) {
60     return std::min(height * kAmbientHeightFactor * kAmbientGeomFactor, kMaxAmbientRadius);
61 }
AmbientRecipAlpha(float height)62 inline float AmbientRecipAlpha(float height) {
63     return 1.0f + std::max(height * kAmbientHeightFactor, 0.0f);
64 }
65 
66 //////////////////////////////////////////////////////////////////////////////
67 // Circle Data
68 //
69 // We have two possible cases for geometry for a circle:
70 
71 // In the case of a normal fill, we draw geometry for the circle as an octagon.
72 static const uint16_t gFillCircleIndices[] = {
73         // enter the octagon
74         // clang-format off
75          0, 1, 8, 1, 2, 8,
76          2, 3, 8, 3, 4, 8,
77          4, 5, 8, 5, 6, 8,
78          6, 7, 8, 7, 0, 8,
79         // clang-format on
80 };
81 
82 // For stroked circles, we use two nested octagons.
83 static const uint16_t gStrokeCircleIndices[] = {
84         // enter the octagon
85         // clang-format off
86          0, 1,  9, 0,  9,  8,
87          1, 2, 10, 1, 10,  9,
88          2, 3, 11, 2, 11, 10,
89          3, 4, 12, 3, 12, 11,
90          4, 5, 13, 4, 13, 12,
91          5, 6, 14, 5, 14, 13,
92          6, 7, 15, 6, 15, 14,
93          7, 0,  8, 7,  8, 15,
94         // clang-format on
95 };
96 
97 #define SK_ARRAY_COUNT(a) (sizeof(a) / sizeof((a)[0]))
98 static const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices);
99 static const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices);
100 static const int kVertsPerStrokeCircle = 16;
101 static const int kVertsPerFillCircle = 9;
102 
circle_type_to_vert_count(bool stroked)103 static int circle_type_to_vert_count(bool stroked) {
104     return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle;
105 }
106 
circle_type_to_index_count(bool stroked)107 static int circle_type_to_index_count(bool stroked) {
108     return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle;
109 }
110 
circle_type_to_indices(bool stroked)111 static const uint16_t* circle_type_to_indices(bool stroked) {
112     return stroked ? gStrokeCircleIndices : gFillCircleIndices;
113 }
114 
115 ///////////////////////////////////////////////////////////////////////////////
116 // RoundRect Data
117 //
118 // The geometry for a shadow roundrect is similar to a 9-patch:
119 //    ____________
120 //   |_|________|_|
121 //   | |        | |
122 //   | |        | |
123 //   | |        | |
124 //   |_|________|_|
125 //   |_|________|_|
126 //
127 // However, each corner is rendered as a fan rather than a simple quad, as below. (The diagram
128 // shows the upper part of the upper left corner. The bottom triangle would similarly be split
129 // into two triangles.)
130 //    ________
131 //   |\  \   |
132 //   |  \ \  |
133 //   |    \\ |
134 //   |      \|
135 //   --------
136 //
137 // The center of the fan handles the curve of the corner. For roundrects where the stroke width
138 // is greater than the corner radius, the outer triangles blend from the curve to the straight
139 // sides. Otherwise these triangles will be degenerate.
140 //
141 // In the case where the stroke width is greater than the corner radius and the
142 // blur radius (overstroke), we add additional geometry to mark out the rectangle in the center.
143 // This rectangle extends the coverage values of the center edges of the 9-patch.
144 //    ____________
145 //   |_|________|_|
146 //   | |\ ____ /| |
147 //   | | |    | | |
148 //   | | |____| | |
149 //   |_|/______\|_|
150 //   |_|________|_|
151 //
152 // For filled rrects we reuse the stroke geometry but add an additional quad to the center.
153 
154 static const uint16_t gRRectIndices[] = {
155         // clang-format off
156      // overstroke quads
157      // we place this at the beginning so that we can skip these indices when rendering as filled
158      0, 6, 25, 0, 25, 24,
159      6, 18, 27, 6, 27, 25,
160      18, 12, 26, 18, 26, 27,
161      12, 0, 24, 12, 24, 26,
162 
163      // corners
164      0, 1, 2, 0, 2, 3, 0, 3, 4, 0, 4, 5,
165      6, 11, 10, 6, 10, 9, 6, 9, 8, 6, 8, 7,
166      12, 17, 16, 12, 16, 15, 12, 15, 14, 12, 14, 13,
167      18, 19, 20, 18, 20, 21, 18, 21, 22, 18, 22, 23,
168 
169      // edges
170      0, 5, 11, 0, 11, 6,
171      6, 7, 19, 6, 19, 18,
172      18, 23, 17, 18, 17, 12,
173      12, 13, 1, 12, 1, 0,
174 
175      // fill quad
176      // we place this at the end so that we can skip these indices when rendering as stroked
177      0, 6, 18, 0, 18, 12,
178         // clang-format on
179 };
180 
181 // overstroke count
182 static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gRRectIndices) - 6;
183 // simple stroke count skips overstroke indices
184 static const int kIndicesPerStrokeRRect = kIndicesPerOverstrokeRRect - 6 * 4;
185 // fill count adds final quad to stroke count
186 static const int kIndicesPerFillRRect = kIndicesPerStrokeRRect + 6;
187 static const int kVertsPerStrokeRRect = 24;
188 static const int kVertsPerOverstrokeRRect = 28;
189 static const int kVertsPerFillRRect = 24;
190 
rrect_type_to_vert_count(RRectType type)191 static int rrect_type_to_vert_count(RRectType type) {
192     switch (type) {
193         case kFill_RRectType:
194             return kVertsPerFillRRect;
195         case kStroke_RRectType:
196             return kVertsPerStrokeRRect;
197         case kOverstroke_RRectType:
198             return kVertsPerOverstrokeRRect;
199     }
200     ALOGE("Invalid rect type: %d", type);
201     return -1;
202 }
203 
rrect_type_to_index_count(RRectType type)204 static int rrect_type_to_index_count(RRectType type) {
205     switch (type) {
206         case kFill_RRectType:
207             return kIndicesPerFillRRect;
208         case kStroke_RRectType:
209             return kIndicesPerStrokeRRect;
210         case kOverstroke_RRectType:
211             return kIndicesPerOverstrokeRRect;
212     }
213     ALOGE("Invalid rect type: %d", type);
214     return -1;
215 }
216 
rrect_type_to_indices(RRectType type)217 static const uint16_t* rrect_type_to_indices(RRectType type) {
218     switch (type) {
219         case kFill_RRectType:
220         case kStroke_RRectType:
221             return gRRectIndices + 6 * 4;
222         case kOverstroke_RRectType:
223             return gRRectIndices;
224     }
225     ALOGE("Invalid rect type: %d", type);
226     return nullptr;
227 }
228 
fillInCircleVerts(const Geometry & args,bool isStroked,Mesh::VertexArray<vec2> & position,Mesh::VertexArray<vec4> & shadowColor,Mesh::VertexArray<vec3> & shadowParams)229 static void fillInCircleVerts(const Geometry& args, bool isStroked,
230                               Mesh::VertexArray<vec2>& position,
231                               Mesh::VertexArray<vec4>& shadowColor,
232                               Mesh::VertexArray<vec3>& shadowParams) {
233     vec4 color = args.fColor;
234     float outerRadius = args.fOuterRadius;
235     float innerRadius = args.fInnerRadius;
236     float blurRadius = args.fBlurRadius;
237     float distanceCorrection = outerRadius / blurRadius;
238 
239     const FloatRect& bounds = args.fDevBounds;
240 
241     // The inner radius in the vertex data must be specified in normalized space.
242     innerRadius = innerRadius / outerRadius;
243 
244     vec2 center = vec2(bounds.getWidth() / 2.0f, bounds.getHeight() / 2.0f);
245     float halfWidth = 0.5f * bounds.getWidth();
246     float octOffset = 0.41421356237f; // sqrt(2) - 1
247     int vertexCount = 0;
248 
249     position[vertexCount] = center + vec2(-octOffset * halfWidth, -halfWidth);
250     shadowColor[vertexCount] = color;
251     shadowParams[vertexCount] = vec3(-octOffset, -1, distanceCorrection);
252     vertexCount++;
253 
254     position[vertexCount] = center + vec2(octOffset * halfWidth, -halfWidth);
255     shadowColor[vertexCount] = color;
256     shadowParams[vertexCount] = vec3(octOffset, -1, distanceCorrection);
257     vertexCount++;
258 
259     position[vertexCount] = center + vec2(halfWidth, -octOffset * halfWidth);
260     shadowColor[vertexCount] = color;
261     shadowParams[vertexCount] = vec3(1, -octOffset, distanceCorrection);
262     vertexCount++;
263 
264     position[vertexCount] = center + vec2(halfWidth, octOffset * halfWidth);
265     shadowColor[vertexCount] = color;
266     shadowParams[vertexCount] = vec3(1, octOffset, distanceCorrection);
267     vertexCount++;
268 
269     position[vertexCount] = center + vec2(octOffset * halfWidth, halfWidth);
270     shadowColor[vertexCount] = color;
271     shadowParams[vertexCount] = vec3(octOffset, 1, distanceCorrection);
272     vertexCount++;
273 
274     position[vertexCount] = center + vec2(-octOffset * halfWidth, halfWidth);
275     shadowColor[vertexCount] = color;
276     shadowParams[vertexCount] = vec3(-octOffset, 1, distanceCorrection);
277     vertexCount++;
278 
279     position[vertexCount] = center + vec2(-halfWidth, octOffset * halfWidth);
280     shadowColor[vertexCount] = color;
281     shadowParams[vertexCount] = vec3(-1, octOffset, distanceCorrection);
282     vertexCount++;
283 
284     position[vertexCount] = center + vec2(-halfWidth, -octOffset * halfWidth);
285     shadowColor[vertexCount] = color;
286     shadowParams[vertexCount] = vec3(-1, -octOffset, distanceCorrection);
287     vertexCount++;
288 
289     if (isStroked) {
290         // compute the inner ring
291 
292         // cosine and sine of pi/8
293         float c = 0.923579533f;
294         float s = 0.382683432f;
295         float r = args.fInnerRadius;
296 
297         position[vertexCount] = center + vec2(-s * r, -c * r);
298         shadowColor[vertexCount] = color;
299         shadowParams[vertexCount] = vec3(-s * innerRadius, -c * innerRadius, distanceCorrection);
300         vertexCount++;
301 
302         position[vertexCount] = center + vec2(s * r, -c * r);
303         shadowColor[vertexCount] = color;
304         shadowParams[vertexCount] = vec3(s * innerRadius, -c * innerRadius, distanceCorrection);
305         vertexCount++;
306 
307         position[vertexCount] = center + vec2(c * r, -s * r);
308         shadowColor[vertexCount] = color;
309         shadowParams[vertexCount] = vec3(c * innerRadius, -s * innerRadius, distanceCorrection);
310         vertexCount++;
311 
312         position[vertexCount] = center + vec2(c * r, s * r);
313         shadowColor[vertexCount] = color;
314         shadowParams[vertexCount] = vec3(c * innerRadius, s * innerRadius, distanceCorrection);
315         vertexCount++;
316 
317         position[vertexCount] = center + vec2(s * r, c * r);
318         shadowColor[vertexCount] = color;
319         shadowParams[vertexCount] = vec3(s * innerRadius, c * innerRadius, distanceCorrection);
320         vertexCount++;
321 
322         position[vertexCount] = center + vec2(-s * r, c * r);
323         shadowColor[vertexCount] = color;
324         shadowParams[vertexCount] = vec3(-s * innerRadius, c * innerRadius, distanceCorrection);
325         vertexCount++;
326 
327         position[vertexCount] = center + vec2(-c * r, s * r);
328         shadowColor[vertexCount] = color;
329         shadowParams[vertexCount] = vec3(-c * innerRadius, s * innerRadius, distanceCorrection);
330         vertexCount++;
331 
332         position[vertexCount] = center + vec2(-c * r, -s * r);
333         shadowColor[vertexCount] = color;
334         shadowParams[vertexCount] = vec3(-c * innerRadius, -s * innerRadius, distanceCorrection);
335         vertexCount++;
336     } else {
337         // filled
338         position[vertexCount] = center;
339         shadowColor[vertexCount] = color;
340         shadowParams[vertexCount] = vec3(0, 0, distanceCorrection);
341         vertexCount++;
342     }
343 }
344 
fillInRRectVerts(const Geometry & args,Mesh::VertexArray<vec2> & position,Mesh::VertexArray<vec4> & shadowColor,Mesh::VertexArray<vec3> & shadowParams)345 static void fillInRRectVerts(const Geometry& args, Mesh::VertexArray<vec2>& position,
346                              Mesh::VertexArray<vec4>& shadowColor,
347                              Mesh::VertexArray<vec3>& shadowParams) {
348     vec4 color = args.fColor;
349     float outerRadius = args.fOuterRadius;
350 
351     const FloatRect& bounds = args.fDevBounds;
352 
353     float umbraInset = args.fUmbraInset;
354     float minDim = 0.5f * std::min(bounds.getWidth(), bounds.getHeight());
355     if (umbraInset > minDim) {
356         umbraInset = minDim;
357     }
358 
359     float xInner[4] = {bounds.left + umbraInset, bounds.right - umbraInset,
360                        bounds.left + umbraInset, bounds.right - umbraInset};
361     float xMid[4] = {bounds.left + outerRadius, bounds.right - outerRadius,
362                      bounds.left + outerRadius, bounds.right - outerRadius};
363     float xOuter[4] = {bounds.left, bounds.right, bounds.left, bounds.right};
364     float yInner[4] = {bounds.top + umbraInset, bounds.top + umbraInset, bounds.bottom - umbraInset,
365                        bounds.bottom - umbraInset};
366     float yMid[4] = {bounds.top + outerRadius, bounds.top + outerRadius,
367                      bounds.bottom - outerRadius, bounds.bottom - outerRadius};
368     float yOuter[4] = {bounds.top, bounds.top, bounds.bottom, bounds.bottom};
369 
370     float blurRadius = args.fBlurRadius;
371 
372     // In the case where we have to inset more for the umbra, our two triangles in the
373     // corner get skewed to a diamond rather than a square. To correct for that,
374     // we also skew the vectors we send to the shader that help define the circle.
375     // By doing so, we end up with a quarter circle in the corner rather than the
376     // elliptical curve.
377 
378     // This is a bit magical, but it gives us the correct results at extrema:
379     //   a) umbraInset == outerRadius produces an orthogonal vector
380     //   b) outerRadius == 0 produces a diagonal vector
381     // And visually the corner looks correct.
382     vec2 outerVec = vec2(outerRadius - umbraInset, -outerRadius - umbraInset);
383     outerVec = normalize(outerVec);
384     // We want the circle edge to fall fractionally along the diagonal at
385     //      (sqrt(2)*(umbraInset - outerRadius) + outerRadius)/sqrt(2)*umbraInset
386     //
387     // Setting the components of the diagonal offset to the following value will give us that.
388     float diagVal = umbraInset / (SK_ScalarSqrt2 * (outerRadius - umbraInset) - outerRadius);
389     vec2 diagVec = vec2(diagVal, diagVal);
390     float distanceCorrection = umbraInset / blurRadius;
391 
392     int vertexCount = 0;
393     // build corner by corner
394     for (int i = 0; i < 4; ++i) {
395         // inner point
396         position[vertexCount] = vec2(xInner[i], yInner[i]);
397         shadowColor[vertexCount] = color;
398         shadowParams[vertexCount] = vec3(0, 0, distanceCorrection);
399         vertexCount++;
400 
401         // outer points
402         position[vertexCount] = vec2(xOuter[i], yInner[i]);
403         shadowColor[vertexCount] = color;
404         shadowParams[vertexCount] = vec3(0, -1, distanceCorrection);
405         vertexCount++;
406 
407         position[vertexCount] = vec2(xOuter[i], yMid[i]);
408         shadowColor[vertexCount] = color;
409         shadowParams[vertexCount] = vec3(outerVec.x, outerVec.y, distanceCorrection);
410         vertexCount++;
411 
412         position[vertexCount] = vec2(xOuter[i], yOuter[i]);
413         shadowColor[vertexCount] = color;
414         shadowParams[vertexCount] = vec3(diagVec.x, diagVec.y, distanceCorrection);
415         vertexCount++;
416 
417         position[vertexCount] = vec2(xMid[i], yOuter[i]);
418         shadowColor[vertexCount] = color;
419         shadowParams[vertexCount] = vec3(outerVec.x, outerVec.y, distanceCorrection);
420         vertexCount++;
421 
422         position[vertexCount] = vec2(xInner[i], yOuter[i]);
423         shadowColor[vertexCount] = color;
424         shadowParams[vertexCount] = vec3(0, -1, distanceCorrection);
425         vertexCount++;
426     }
427 
428     // Add the additional vertices for overstroked rrects.
429     // Effectively this is an additional stroked rrect, with its
430     // parameters equal to those in the center of the 9-patch. This will
431     // give constant values across this inner ring.
432     if (kOverstroke_RRectType == args.fType) {
433         float inset = umbraInset + args.fInnerRadius;
434 
435         // TL
436         position[vertexCount] = vec2(bounds.left + inset, bounds.top + inset);
437         shadowColor[vertexCount] = color;
438         shadowParams[vertexCount] = vec3(0, 0, distanceCorrection);
439         vertexCount++;
440 
441         // TR
442         position[vertexCount] = vec2(bounds.right - inset, bounds.top + inset);
443         shadowColor[vertexCount] = color;
444         shadowParams[vertexCount] = vec3(0, 0, distanceCorrection);
445         vertexCount++;
446 
447         // BL
448         position[vertexCount] = vec2(bounds.left + inset, bounds.bottom - inset);
449         shadowColor[vertexCount] = color;
450         shadowParams[vertexCount] = vec3(0, 0, distanceCorrection);
451         vertexCount++;
452 
453         // BR
454         position[vertexCount] = vec2(bounds.right - inset, bounds.bottom - inset);
455         shadowColor[vertexCount] = color;
456         shadowParams[vertexCount] = vec3(0, 0, distanceCorrection);
457         vertexCount++;
458     }
459 }
460 
getVertexCountForGeometry(const Geometry & shadowGeometry)461 int getVertexCountForGeometry(const Geometry& shadowGeometry) {
462     if (shadowGeometry.fIsCircle) {
463         return circle_type_to_vert_count(shadowGeometry.fType);
464     }
465 
466     return rrect_type_to_vert_count(shadowGeometry.fType);
467 }
468 
getIndexCountForGeometry(const Geometry & shadowGeometry)469 int getIndexCountForGeometry(const Geometry& shadowGeometry) {
470     if (shadowGeometry.fIsCircle) {
471         return circle_type_to_index_count(kStroke_RRectType == shadowGeometry.fType);
472     }
473 
474     return rrect_type_to_index_count(shadowGeometry.fType);
475 }
476 
fillVerticesForGeometry(const Geometry & shadowGeometry,int,Mesh::VertexArray<vec2> position,Mesh::VertexArray<vec4> shadowColor,Mesh::VertexArray<vec3> shadowParams)477 void fillVerticesForGeometry(const Geometry& shadowGeometry, int /* vertexCount */,
478                              Mesh::VertexArray<vec2> position, Mesh::VertexArray<vec4> shadowColor,
479                              Mesh::VertexArray<vec3> shadowParams) {
480     if (shadowGeometry.fIsCircle) {
481         fillInCircleVerts(shadowGeometry, shadowGeometry.fIsStroked, position, shadowColor,
482                           shadowParams);
483     } else {
484         fillInRRectVerts(shadowGeometry, position, shadowColor, shadowParams);
485     }
486 }
487 
fillIndicesForGeometry(const Geometry & shadowGeometry,int indexCount,int startingVertexOffset,uint16_t * indices)488 void fillIndicesForGeometry(const Geometry& shadowGeometry, int indexCount,
489                             int startingVertexOffset, uint16_t* indices) {
490     if (shadowGeometry.fIsCircle) {
491         const uint16_t* primIndices = circle_type_to_indices(shadowGeometry.fIsStroked);
492         for (int i = 0; i < indexCount; ++i) {
493             indices[i] = primIndices[i] + startingVertexOffset;
494         }
495     } else {
496         const uint16_t* primIndices = rrect_type_to_indices(shadowGeometry.fType);
497         for (int i = 0; i < indexCount; ++i) {
498             indices[i] = primIndices[i] + startingVertexOffset;
499         }
500     }
501 }
502 
GetSpotParams(float occluderZ,float lightX,float lightY,float lightZ,float lightRadius,float & blurRadius,float & scale,vec2 & translate)503 inline void GetSpotParams(float occluderZ, float lightX, float lightY, float lightZ,
504                           float lightRadius, float& blurRadius, float& scale, vec2& translate) {
505     float zRatio = divide_and_pin(occluderZ, lightZ - occluderZ, 0.0f, 0.95f);
506     blurRadius = lightRadius * zRatio;
507     scale = divide_and_pin(lightZ, lightZ - occluderZ, 1.0f, 1.95f);
508     translate.x = -zRatio * lightX;
509     translate.y = -zRatio * lightY;
510 }
511 
getShadowGeometry(const vec4 & color,const FloatRect & devRect,float devRadius,float blurRadius,float insetWidth)512 static std::unique_ptr<Geometry> getShadowGeometry(const vec4& color, const FloatRect& devRect,
513                                                    float devRadius, float blurRadius,
514                                                    float insetWidth) {
515     // An insetWidth > 1/2 rect width or height indicates a simple fill.
516     const bool isCircle = ((devRadius >= devRect.getWidth()) && (devRadius >= devRect.getHeight()));
517 
518     FloatRect bounds = devRect;
519     float innerRadius = 0.0f;
520     float outerRadius = devRadius;
521     float umbraInset;
522 
523     RRectType type = kFill_RRectType;
524     if (isCircle) {
525         umbraInset = 0;
526     } else {
527         umbraInset = std::max(outerRadius, blurRadius);
528     }
529 
530     // If stroke is greater than width or height, this is still a fill,
531     // otherwise we compute stroke params.
532     if (isCircle) {
533         innerRadius = devRadius - insetWidth;
534         type = innerRadius > 0 ? kStroke_RRectType : kFill_RRectType;
535     } else {
536         if (insetWidth <= 0.5f * std::min(devRect.getWidth(), devRect.getHeight())) {
537             // We don't worry about a real inner radius, we just need to know if we
538             // need to create overstroke vertices.
539             innerRadius = std::max(insetWidth - umbraInset, 0.0f);
540             type = innerRadius > 0 ? kOverstroke_RRectType : kStroke_RRectType;
541         }
542     }
543     const bool isStroked = (kStroke_RRectType == type);
544     return std::make_unique<Geometry>(Geometry{color, outerRadius, umbraInset, innerRadius,
545                                                blurRadius, bounds, type, isCircle, isStroked});
546 }
547 
getAmbientShadowGeometry(const FloatRect & casterRect,float casterCornerRadius,float casterZ,bool casterIsTranslucent,const vec4 & ambientColor)548 std::unique_ptr<Geometry> getAmbientShadowGeometry(const FloatRect& casterRect,
549                                                    float casterCornerRadius, float casterZ,
550                                                    bool casterIsTranslucent,
551                                                    const vec4& ambientColor) {
552     float devSpaceInsetWidth = AmbientBlurRadius(casterZ);
553     const float umbraRecipAlpha = AmbientRecipAlpha(casterZ);
554     const float devSpaceAmbientBlur = devSpaceInsetWidth * umbraRecipAlpha;
555 
556     // Outset the shadow rrect to the border of the penumbra
557     float ambientPathOutset = devSpaceInsetWidth;
558     FloatRect outsetRect(casterRect);
559     outsetRect.left -= ambientPathOutset;
560     outsetRect.top -= ambientPathOutset;
561     outsetRect.right += ambientPathOutset;
562     outsetRect.bottom += ambientPathOutset;
563 
564     float outsetRad = casterCornerRadius + ambientPathOutset;
565     if (casterIsTranslucent) {
566         // set a large inset to force a fill
567         devSpaceInsetWidth = outsetRect.getWidth();
568     }
569 
570     return getShadowGeometry(ambientColor, outsetRect, std::abs(outsetRad), devSpaceAmbientBlur,
571                              std::abs(devSpaceInsetWidth));
572 }
573 
getSpotShadowGeometry(const FloatRect & casterRect,float casterCornerRadius,float casterZ,bool casterIsTranslucent,const vec4 & spotColor,const vec3 & lightPosition,float lightRadius)574 std::unique_ptr<Geometry> getSpotShadowGeometry(const FloatRect& casterRect,
575                                                 float casterCornerRadius, float casterZ,
576                                                 bool casterIsTranslucent, const vec4& spotColor,
577                                                 const vec3& lightPosition, float lightRadius) {
578     float devSpaceSpotBlur;
579     float spotScale;
580     vec2 spotOffset;
581     GetSpotParams(casterZ, lightPosition.x, lightPosition.y, lightPosition.z, lightRadius,
582                   devSpaceSpotBlur, spotScale, spotOffset);
583     // handle scale of radius due to CTM
584     const float srcSpaceSpotBlur = devSpaceSpotBlur;
585 
586     // Adjust translate for the effect of the scale.
587     spotOffset.x += spotScale;
588     spotOffset.y += spotScale;
589 
590     // Compute the transformed shadow rect
591     ui::Transform shadowTransform;
592     shadowTransform.set(spotOffset.x, spotOffset.y);
593     shadowTransform.set(spotScale, 0, 0, spotScale);
594     FloatRect spotShadowRect = shadowTransform.transform(casterRect);
595     float spotShadowRadius = casterCornerRadius * spotScale;
596 
597     // Compute the insetWidth
598     float blurOutset = srcSpaceSpotBlur;
599     float insetWidth = blurOutset;
600     if (casterIsTranslucent) {
601         // If transparent, just do a fill
602         insetWidth += spotShadowRect.getWidth();
603     } else {
604         // For shadows, instead of using a stroke we specify an inset from the penumbra
605         // border. We want to extend this inset area so that it meets up with the caster
606         // geometry. The inset geometry will by default already be inset by the blur width.
607         //
608         // We compare the min and max corners inset by the radius between the original
609         // rrect and the shadow rrect. The distance between the two plus the difference
610         // between the scaled radius and the original radius gives the distance from the
611         // transformed shadow shape to the original shape in that corner. The max
612         // of these gives the maximum distance we need to cover.
613         //
614         // Since we are outsetting by 1/2 the blur distance, we just add the maxOffset to
615         // that to get the full insetWidth.
616         float maxOffset;
617         if (casterCornerRadius <= 0.f) {
618             // Manhattan distance works better for rects
619             maxOffset = std::max(std::max(std::abs(spotShadowRect.left - casterRect.left),
620                                           std::abs(spotShadowRect.top - casterRect.top)),
621                                  std::max(std::abs(spotShadowRect.right - casterRect.right),
622                                           std::abs(spotShadowRect.bottom - casterRect.bottom)));
623         } else {
624             float dr = spotShadowRadius - casterCornerRadius;
625             vec2 upperLeftOffset = vec2(spotShadowRect.left - casterRect.left + dr,
626                                         spotShadowRect.top - casterRect.top + dr);
627             vec2 lowerRightOffset = vec2(spotShadowRect.right - casterRect.right - dr,
628                                          spotShadowRect.bottom - casterRect.bottom - dr);
629             maxOffset = sqrt(std::max(dot(upperLeftOffset, lowerRightOffset),
630                                       dot(lowerRightOffset, lowerRightOffset))) +
631                     dr;
632         }
633         insetWidth += std::max(blurOutset, maxOffset);
634     }
635 
636     // Outset the shadow rrect to the border of the penumbra
637     spotShadowRadius += blurOutset;
638     spotShadowRect.left -= blurOutset;
639     spotShadowRect.top -= blurOutset;
640     spotShadowRect.right += blurOutset;
641     spotShadowRect.bottom += blurOutset;
642 
643     return getShadowGeometry(spotColor, spotShadowRect, std::abs(spotShadowRadius),
644                              2.0f * devSpaceSpotBlur, std::abs(insetWidth));
645 }
646 
fillShadowTextureData(uint8_t * data,size_t shadowTextureWidth)647 void fillShadowTextureData(uint8_t* data, size_t shadowTextureWidth) {
648     for (int i = 0; i < shadowTextureWidth; i++) {
649         const float d = 1 - i / ((shadowTextureWidth * 1.0f) - 1.0f);
650         data[i] = static_cast<uint8_t>((exp(-4.0f * d * d) - 0.018f) * 255);
651     }
652 }
653 
654 } // namespace gl
655 } // namespace renderengine
656 } // namespace android
657