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