Fluid
Loading...
Searching...
No Matches
skia_shadow.h
Go to the documentation of this file.
1/*
2 * This file is part of Fluid.
3 *
4 * Copyright (C) 2025 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
5 * Copyright (C) 2024-2025 hypengw <hypengwip@gmail.com>
6 *
7 * $BEGIN_LICENSE:MPL2$
8 *
9 * This Source Code Form is subject to the terms of the Mozilla Public
10 * License, v. 2.0. If a copy of the MPL was not distributed with this
11 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
12 *
13 * $END_LICENSE$
14 */
15
16#include "geometry.h"
17
18// https://github.com/google/skia/blob/canvaskit/0.38.2/src/gpu/ganesh/ops/ShadowRRectOp.cpp
19
20namespace Fluid::Skia {
21
22using scalar = float;
23
24template <typename T>
25static constexpr const T &SkTPin(const T &x, const T &lo, const T &hi)
26{
27 return std::max(lo, std::min(x, hi));
28}
29
30constexpr auto sk_double_round(double x)
31{
32 return std::floor((x) + 0.5);
33}
34constexpr auto sk_float_round(double x)
35{
36 return (float)std::floor((x) + 0.5);
37}
38
39template <typename T, std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
40static inline constexpr bool SkIsNaN(T x)
41{
42 return x != x;
43}
44
45#if defined(_MSC_VER) && !defined(__clang__)
46# define SK_CHECK_NAN(resultVal) \
47 if (SkIsNaN(x)) { \
48 return resultVal; \
49 }
50#else
51# define SK_CHECK_NAN(resultVal)
52#endif
53
54inline constexpr int SK_MaxS32FitsInFloat = 2147483520;
56static constexpr int sk_float_saturate2int(float x)
57{
61 return (int)x;
62}
63constexpr auto SkScalarRoundToInt(double x)
64{
66}
67
68static constexpr auto kAmbientHeightFactor = 1.0f / 128.0f;
69static constexpr auto kAmbientGeomFactor = 64.0f;
70// Assuming that we have a light height of 600 for the spot shadow,
71// the spot values will reach their maximum at a height of approximately 292.3077.
72// We'll round up to 300 to keep it simple.
74
75static inline float divide_and_pin(float numer, float denom, float min, float max)
76{
77 float result = SkTPin(numer / denom, min, max);
78 // ensure that SkTPin handled non-finites correctly
79 Q_ASSERT(result >= min && result <= max);
80 return result;
81}
82constexpr auto SK_Scalar1{ 1.0f };
83constexpr auto SK_ScalarNearlyZero{ (SK_Scalar1 / (1 << 12)) };
84
86{
88}
90{
91 return 1.0f + std::max(height * kAmbientHeightFactor, 0.0f);
92}
93
94inline scalar SpotBlurRadius(scalar occluderZ, scalar lightZ, scalar lightRadius)
95{
96 return lightRadius * divide_and_pin(occluderZ, lightZ - occluderZ, 0.0f, 0.95f);
97}
98
99inline void GetSpotParams(scalar occluderZ, scalar lightX, scalar lightY, scalar lightZ,
100 scalar lightRadius, scalar *blurRadius, scalar *scale,
101 QVector2D *translate)
102{
103 scalar zRatio = divide_and_pin(occluderZ, lightZ - occluderZ, 0.0f, 0.95f);
104 *blurRadius = lightRadius * zRatio;
105 *scale = divide_and_pin(lightZ, lightZ - occluderZ, 1.0f, 1.95f);
106 *translate = QVector2D(-zRatio * lightX, -zRatio * lightY);
107}
108
109inline void GetDirectionalParams(scalar occluderZ, scalar lightX, scalar lightY, scalar lightZ,
110 scalar lightRadius, scalar *blurRadius, scalar *scale,
111 QVector2D *translate)
112{
113 *blurRadius = lightRadius * occluderZ;
114 *scale = 1;
115 // Max z-ratio is "max expected elevation"/"min allowable z"
116 constexpr scalar kMaxZRatio = 64 / SK_ScalarNearlyZero;
117 scalar zRatio = divide_and_pin(occluderZ, lightZ, 0.0f, kMaxZRatio);
118 *translate = QVector2D(-zRatio * lightX, -zRatio * lightY);
119}
120
122{
123public:
124 using scalar = float;
130 // An insetWidth > 1/2 rect width or height indicates a simple fill.
131 ShadowCircularRRectOp(QRgb color, const QRectF &devRect, float devRadius, bool isCircle,
132 float blurRadius, float insetWidth)
133 : fIndexPtr(nullptr)
134 {
135 QRectF bounds = devRect;
136 Q_ASSERT(insetWidth > 0);
137 scalar innerRadius = 0.0f;
138 scalar outerRadius = devRadius;
139 scalar umbraInset;
140
142 if (isCircle) {
143 umbraInset = 0;
144 } else {
145 umbraInset = std::max(outerRadius, blurRadius);
146 }
147
148 // If stroke is greater than width or height, this is still a fill,
149 // otherwise we compute stroke params.
150 if (isCircle) {
151 innerRadius = devRadius - insetWidth;
152 type = innerRadius > 0 ? kStroke_RRectType : kFill_RRectType;
153 } else {
154 if (insetWidth <= 0.5f * std::min(devRect.width(), devRect.height())) {
155 // We don't worry about a real inner radius, we just need to know if we
156 // need to create overstroke vertices.
157 innerRadius = std::max(insetWidth - umbraInset, 0.0f);
158 type = innerRadius > 0 ? kOverstroke_RRectType : kStroke_RRectType;
159 }
160 }
161
162 // this->setBounds(bounds, HasAABloat::kNo, IsHairline::kNo);
163
164 fGeoData = (Geometry{ color, outerRadius, umbraInset, innerRadius, blurRadius, bounds, type,
165 isCircle });
166 if (isCircle) {
170 } else {
174 }
175 }
187
188 // In the case of a normal fill, we draw geometry for the circle as an octagon.
189 static constexpr uint16_t gFillCircleIndices[] = {
190 // enter the octagon
191 // clang-format off
192 0, 1, 8, 1, 2, 8,
193 2, 3, 8, 3, 4, 8,
194 4, 5, 8, 5, 6, 8,
195 6, 7, 8, 7, 0, 8,
196 // clang-format on
197 };
198
199 // For stroked circles, we use two nested octagons.
200 static constexpr uint16_t gStrokeCircleIndices[] = {
201 // enter the octagon
202 // clang-format off
203 0, 1, 9, 0, 9, 8,
204 1, 2, 10, 1, 10, 9,
205 2, 3, 11, 2, 11, 10,
206 3, 4, 12, 3, 12, 11,
207 4, 5, 13, 4, 13, 12,
208 5, 6, 14, 5, 14, 13,
209 6, 7, 15, 6, 15, 14,
210 7, 0, 8, 7, 8, 15,
211 // clang-format on
212 };
213
214 static constexpr uint16_t gRRectIndices[] = {
215 // clang-format off
216 // overstroke quads
217 // we place this at the beginning so that we can skip these indices when rendering as filled
218 0, 6, 25, 0, 25, 24,
219 6, 18, 27, 6, 27, 25,
220 18, 12, 26, 18, 26, 27,
221 12, 0, 24, 12, 24, 26,
222
223 // corners
224 0, 1, 2, 0, 2, 3, 0, 3, 4, 0, 4, 5,
225 6, 11, 10, 6, 10, 9, 6, 9, 8, 6, 8, 7,
226 12, 17, 16, 12, 16, 15, 12, 15, 14, 12, 14, 13,
227 18, 19, 20, 18, 20, 21, 18, 21, 22, 18, 22, 23,
228
229 // edges
230 0, 5, 11, 0, 11, 6,
231 6, 7, 19, 6, 19, 18,
232 18, 23, 17, 18, 17, 12,
233 12, 13, 1, 12, 1, 0,
234
235 // fill quad
236 // we place this at the end so that we can skip these indices when rendering as stroked
237 0, 6, 18, 0, 18, 12,
238 // clang-format on
239 };
240
241 static constexpr float SK_FloatSqrt2 = 1.41421356f;
242
243 // overstroke count
244 static constexpr int kIndicesPerOverstrokeRRect = std::size(gRRectIndices) - 6;
245 // simple stroke count skips overstroke indices
247 // fill count adds final quad to stroke count
249 static constexpr int kVertsPerStrokeRRect = 24;
250 static constexpr int kVertsPerOverstrokeRRect = 28;
251 static constexpr int kVertsPerFillRRect = 24;
252
253 static constexpr int kIndicesPerFillCircle = std::size(gFillCircleIndices);
254 static constexpr int kIndicesPerStrokeCircle = std::size(gStrokeCircleIndices);
255 static constexpr int kVertsPerStrokeCircle = 16;
256 static constexpr int kVertsPerFillCircle = 9;
257
258 static int circle_type_to_vert_count(bool stroked)
259 {
261 }
262
263 static int circle_type_to_index_count(bool stroked)
264 {
266 }
267
268 static const uint16_t *circle_type_to_indices(bool stroked)
269 {
270 return stroked ? gStrokeCircleIndices : gFillCircleIndices;
271 }
272
274 {
275 switch (type) {
276 case kFill_RRectType:
277 return kVertsPerFillRRect;
282 }
283 Q_UNREACHABLE();
284 }
285
287 {
288 switch (type) {
289 case kFill_RRectType:
295 }
296 Q_UNREACHABLE();
297 }
298
299 static const uint16_t *rrect_type_to_indices(RRectType type)
300 {
301 switch (type) {
302 case kFill_RRectType:
304 return gRRectIndices + 6 * 4;
306 return gRRectIndices;
307 }
308 Q_UNREACHABLE();
309 }
310
311public:
312 void fillInCircleVerts(bool isStroked, SceneGraph::ShadowVertex **vp) const
313 {
314 const auto &args = this->fGeoData;
315 QRgb color = args.color;
316 scalar outerRadius = args.outer_radius;
317 scalar innerRadius = args.inner_radius;
318 scalar blurRadius = args.blur_radius;
319 scalar distanceCorrection = outerRadius / blurRadius;
320
321 const QRectF &bounds = args.dev_bounds;
322
323 // The inner radius in the vertex data must be specified in normalized space.
324 innerRadius = innerRadius / outerRadius;
325
326 auto center = QVector2D(bounds.center().x(), bounds.center().y());
327 scalar halfWidth = 0.5f * bounds.width();
328 scalar octOffset = 0.41421356237f; // sqrt(2) - 1
329
330 auto v = vp[0];
331
332 v->setPoint(center + QVector2D(-octOffset * halfWidth, -halfWidth));
333 v->setColor(color);
334 v->setOffset({ -octOffset, -1 });
335 v->distance_correction = distanceCorrection;
336 v++;
337
338 // Second vertex
339 v->setPoint(center + QVector2D(octOffset * halfWidth, -halfWidth));
340 v->setColor(color);
341 v->setOffset({ octOffset, -1 });
342 v->distance_correction = distanceCorrection;
343 v++;
344
345 // Third vertex
346 v->setPoint(center + QVector2D(halfWidth, -octOffset * halfWidth));
347 v->setColor(color);
348 v->setOffset({ 1, -octOffset });
349 v->distance_correction = distanceCorrection;
350 v++;
351
352 // Fourth vertex
353 v->setPoint(center + QVector2D(halfWidth, octOffset * halfWidth));
354 v->setColor(color);
355 v->setOffset({ 1, octOffset });
356 v->distance_correction = distanceCorrection;
357 v++;
358
359 // Fifth vertex
360 v->setPoint(center + QVector2D(octOffset * halfWidth, halfWidth));
361 v->setColor(color);
362 v->setOffset({ octOffset, 1 });
363 v->distance_correction = distanceCorrection;
364 v++;
365
366 // Sixth vertex
367 v->setPoint(center + QVector2D(-octOffset * halfWidth, halfWidth));
368 v->setColor(color);
369 v->setOffset({ -octOffset, 1 });
370 v->distance_correction = distanceCorrection;
371 v++;
372
373 // Seventh vertex
374 v->setPoint(center + QVector2D(-halfWidth, octOffset * halfWidth));
375 v->setColor(color);
376 v->setOffset({ -1, octOffset });
377 v->distance_correction = distanceCorrection;
378 v++;
379
380 // Eighth vertex
381 v->setPoint(center + QVector2D(-halfWidth, -octOffset * halfWidth));
382 v->setColor(color);
383 v->setOffset({ -1, -octOffset });
384 v->distance_correction = distanceCorrection;
385 v++;
386
387 if (isStroked) {
388 // compute the inner ring
389 // cosine and sine of pi/8
390 scalar c = 0.923579533f;
391 scalar s = 0.382683432f;
392 scalar r = args.inner_radius;
393 v->setPoint(center + QVector2D(-s * r, -c * r));
394 v->setColor(color);
395 v->setOffset({ -s * innerRadius, -c * innerRadius });
396 v->distance_correction = distanceCorrection;
397 v++;
398
399 // Second vertex
400 v->setPoint(center + QVector2D(s * r, -c * r));
401 v->setColor(color);
402 v->setOffset({ s * innerRadius, -c * innerRadius });
403 v->distance_correction = distanceCorrection;
404 v++;
405
406 // Third vertex
407 v->setPoint(center + QVector2D(c * r, -s * r));
408 v->setColor(color);
409 v->setOffset({ c * innerRadius, -s * innerRadius });
410 v->distance_correction = distanceCorrection;
411 v++;
412
413 // Fourth vertex
414 v->setPoint(center + QVector2D(c * r, s * r));
415 v->setColor(color);
416 v->setOffset({ c * innerRadius, s * innerRadius });
417 v->distance_correction = distanceCorrection;
418 v++;
419
420 // Fifth vertex
421 v->setPoint(center + QVector2D(s * r, c * r));
422 v->setColor(color);
423 v->setOffset({ s * innerRadius, c * innerRadius });
424 v->distance_correction = distanceCorrection;
425 v++;
426
427 // Sixth vertex
428 v->setPoint(center + QVector2D(-s * r, c * r));
429 v->setColor(color);
430 v->setOffset({ -s * innerRadius, c * innerRadius });
431 v->distance_correction = distanceCorrection;
432 v++;
433
434 // Seventh vertex
435 v->setPoint(center + QVector2D(-c * r, s * r));
436 v->setColor(color);
437 v->setOffset({ -c * innerRadius, s * innerRadius });
438 v->distance_correction = distanceCorrection;
439 v++;
440
441 // Eighth vertex
442 v->setPoint(center + QVector2D(-c * r, -s * r));
443 v->setColor(color);
444 v->setOffset({ -c * innerRadius, -s * innerRadius });
445 v->distance_correction = distanceCorrection;
446 v++;
447 } else {
448 // filled
449 v->setPoint(center);
450 v->setColor(color);
451 v->setOffset({ 0, 0 });
452 v->distance_correction = distanceCorrection;
453 v++;
454 }
455
456 vp[0] = v;
457 }
459 {
460 const Geometry &args = this->fGeoData;
461 QRgb color = args.color;
462 scalar outer_radius = args.outer_radius;
463
464 const QRectF &bounds = args.dev_bounds;
465
466 scalar umbra_inset = args.umbra_inset;
467 scalar min_dim = 0.5f * std::min(bounds.width(), bounds.height());
468 if (umbra_inset > min_dim) {
469 umbra_inset = min_dim;
470 }
471
472 scalar xInner[4] = { (float)bounds.left() + umbra_inset,
473 (float)bounds.right() - umbra_inset,
474 (float)bounds.left() + umbra_inset,
475 (float)bounds.right() - umbra_inset };
476 scalar xMid[4] = { (float)bounds.left() + outer_radius,
477 (float)bounds.right() - outer_radius,
478 (float)bounds.left() + outer_radius,
479 (float)bounds.right() - outer_radius };
480 scalar xOuter[4] = { (float)bounds.left(), (float)bounds.right(), (float)bounds.left(),
481 (float)bounds.right() };
482 scalar yInner[4] = { (float)bounds.top() + umbra_inset, (float)bounds.top() + umbra_inset,
483 (float)bounds.bottom() - umbra_inset,
484 (float)bounds.bottom() - umbra_inset };
485 scalar yMid[4] = { (float)bounds.top() + outer_radius, (float)bounds.top() + outer_radius,
486 (float)bounds.bottom() - outer_radius,
487 (float)bounds.bottom() - outer_radius };
488 scalar yOuter[4] = { (float)bounds.top(), (float)bounds.top(), (float)bounds.bottom(),
489 (float)bounds.bottom() };
490
491 scalar blurRadius = args.blur_radius;
492
493 // In the case where we have to inset more for the umbra, our two triangles in the
494 // corner get skewed to a diamond rather than a square. To correct for that,
495 // we also skew the vectors we send to the shader that help define the circle.
496 // By doing so, we end up with a quarter circle in the corner rather than the
497 // elliptical curve.
498
499 // This is a bit magical, but it gives us the correct results at extrema:
500 // a) umbraInset == outerRadius produces an orthogonal vector
501 // b) outerRadius == 0 produces a diagonal vector
502 // And visually the corner looks correct.
503 QVector2D outerVec = QVector2D(outer_radius - umbra_inset, -outer_radius - umbra_inset);
504 outerVec.normalize();
505 // We want the circle edge to fall fractionally along the diagonal at
506 // (sqrt(2)*(umbraInset - outerRadius) + outerRadius)/sqrt(2)*umbraInset
507 //
508 // Setting the components of the diagonal offset to the following value will give us that.
509 scalar diag_val =
510 umbra_inset / (SK_FloatSqrt2 * (outer_radius - umbra_inset) - outer_radius);
511 QVector2D diag_vec = QVector2D(diag_val, diag_val);
512 scalar distance_correction = umbra_inset / blurRadius;
513
514 auto v = vp[0];
515 // build corner by corner
516 for (int i = 0; i < 4; ++i) {
517 // inner point
518 v->setPoint(xInner[i], yInner[i]);
519 v->setColor(color);
520 v->setOffset({ 0, 0 });
521 v->distance_correction = distance_correction;
522 v++;
523
524 // outer points
525 v->setPoint(xOuter[i], yInner[i]);
526 v->setColor(color);
527 v->setOffset({ 0, -1 });
528 v->distance_correction = distance_correction;
529 v++;
530
531 v->setPoint(xOuter[i], yMid[i]);
532 v->setColor(color);
533 v->setOffset(outerVec);
534 v->distance_correction = distance_correction;
535 v++;
536
537 v->setPoint(xOuter[i], yOuter[i]);
538 v->setColor(color);
539 v->setOffset(diag_vec);
540 v->distance_correction = distance_correction;
541 v++;
542
543 v->setPoint(xMid[i], yOuter[i]);
544 v->setColor(color);
545 v->setOffset(outerVec);
546 v->distance_correction = distance_correction;
547 v++;
548
549 v->setPoint(xInner[i], yOuter[i]);
550 v->setColor(color);
551 v->setOffset({ 0, -1 });
552 v->distance_correction = distance_correction;
553 v++;
554 }
555
556 // Add the additional vertices for overstroked rrects.
557 // Effectively this is an additional stroked rrect, with its
558 // parameters equal to those in the center of the 9-patch. This will
559 // give constant values across this inner ring.
560 if (kOverstroke_RRectType == args.type) {
561 Q_ASSERT(args.inner_radius > 0.0f);
562
563 scalar inset = umbra_inset + args.inner_radius;
564
565 // TL
566 v->setPoint(bounds.left() + inset, bounds.top() + inset);
567 v->setColor(color);
568 v->setOffset({ 0, 0 });
569 v->distance_correction = distance_correction;
570 v++;
571
572 // TR
573 v->setPoint(bounds.right() - inset, bounds.top() + inset);
574 v->setColor(color);
575 v->setOffset({ 0, 0 });
576 v->distance_correction = distance_correction;
577 v++;
578
579 // BL
580 v->setPoint(bounds.left() + inset, bounds.bottom() - inset);
581 v->setColor(color);
582 v->setOffset({ 0, 0 });
583 v->distance_correction = distance_correction;
584 v++;
585
586 // BR
587 v->setPoint(bounds.right() - inset, bounds.bottom() - inset);
588 v->setColor(color);
589 v->setOffset({ 0, 0 });
590 v->distance_correction = distance_correction;
591 v++;
592 }
593
594 vp[0] = v;
595 }
596
600 const uint16_t *fIndexPtr;
601};
602
603} // namespace Fluid::Skia
Definition skia_shadow.h:122
static int rrect_type_to_index_count(RRectType type)
Definition skia_shadow.h:286
static constexpr int kVertsPerStrokeCircle
Definition skia_shadow.h:255
static constexpr int kVertsPerFillRRect
Definition skia_shadow.h:251
void fillInRRectVerts(SceneGraph::ShadowVertex **vp) const
Definition skia_shadow.h:458
static int circle_type_to_vert_count(bool stroked)
Definition skia_shadow.h:258
static constexpr int kIndicesPerStrokeRRect
Definition skia_shadow.h:246
int fIndexCount
Definition skia_shadow.h:599
void fillInCircleVerts(bool isStroked, SceneGraph::ShadowVertex **vp) const
Definition skia_shadow.h:312
static constexpr int kVertsPerStrokeRRect
Definition skia_shadow.h:249
static constexpr int kIndicesPerOverstrokeRRect
Definition skia_shadow.h:244
static constexpr int kIndicesPerStrokeCircle
Definition skia_shadow.h:254
static constexpr int kIndicesPerFillRRect
Definition skia_shadow.h:248
static const uint16_t * rrect_type_to_indices(RRectType type)
Definition skia_shadow.h:299
RRectType
Definition skia_shadow.h:125
@ kFill_RRectType
Definition skia_shadow.h:126
@ kOverstroke_RRectType
Definition skia_shadow.h:128
@ kStroke_RRectType
Definition skia_shadow.h:127
static int circle_type_to_index_count(bool stroked)
Definition skia_shadow.h:263
float scalar
Definition skia_shadow.h:124
ShadowCircularRRectOp(QRgb color, const QRectF &devRect, float devRadius, bool isCircle, float blurRadius, float insetWidth)
Definition skia_shadow.h:131
static constexpr uint16_t gRRectIndices[]
Definition skia_shadow.h:214
static constexpr uint16_t gStrokeCircleIndices[]
Definition skia_shadow.h:200
static constexpr uint16_t gFillCircleIndices[]
Definition skia_shadow.h:189
int fVertCount
Definition skia_shadow.h:598
static constexpr int kVertsPerOverstrokeRRect
Definition skia_shadow.h:250
static const uint16_t * circle_type_to_indices(bool stroked)
Definition skia_shadow.h:268
static constexpr int kIndicesPerFillCircle
Definition skia_shadow.h:253
static constexpr int kVertsPerFillCircle
Definition skia_shadow.h:256
static constexpr float SK_FloatSqrt2
Definition skia_shadow.h:241
Geometry fGeoData
Definition skia_shadow.h:597
const uint16_t * fIndexPtr
Definition skia_shadow.h:600
static int rrect_type_to_vert_count(RRectType type)
Definition skia_shadow.h:273
Definition skia_shadow.h:20
constexpr int SK_MaxS32FitsInFloat
Definition skia_shadow.h:54
constexpr auto sk_float_round(double x)
Definition skia_shadow.h:34
static constexpr auto kAmbientGeomFactor
Definition skia_shadow.h:69
static constexpr const T & SkTPin(const T &x, const T &lo, const T &hi)
Definition skia_shadow.h:25
static constexpr auto kMaxAmbientRadius
Definition skia_shadow.h:73
constexpr auto SK_Scalar1
Definition skia_shadow.h:82
constexpr auto SK_ScalarNearlyZero
Definition skia_shadow.h:83
void GetSpotParams(scalar occluderZ, scalar lightX, scalar lightY, scalar lightZ, scalar lightRadius, scalar *blurRadius, scalar *scale, QVector2D *translate)
Definition skia_shadow.h:99
scalar AmbientBlurRadius(scalar height)
Definition skia_shadow.h:85
constexpr auto SkScalarRoundToInt(double x)
Definition skia_shadow.h:63
void GetDirectionalParams(scalar occluderZ, scalar lightX, scalar lightY, scalar lightZ, scalar lightRadius, scalar *blurRadius, scalar *scale, QVector2D *translate)
Definition skia_shadow.h:109
constexpr auto sk_double_round(double x)
Definition skia_shadow.h:30
scalar SpotBlurRadius(scalar occluderZ, scalar lightZ, scalar lightRadius)
Definition skia_shadow.h:94
constexpr int SK_MinS32FitsInFloat
Definition skia_shadow.h:55
static constexpr int sk_float_saturate2int(float x)
Definition skia_shadow.h:56
static constexpr auto kAmbientHeightFactor
Definition skia_shadow.h:68
float scalar
Definition skia_shadow.h:22
static float divide_and_pin(float numer, float denom, float min, float max)
Definition skia_shadow.h:75
scalar AmbientRecipAlpha(scalar height)
Definition skia_shadow.h:89
static constexpr bool SkIsNaN(T x)
Definition skia_shadow.h:40
#define SK_CHECK_NAN(resultVal)
Definition skia_shadow.h:51
void setPoint(float x, float y) noexcept
Definition geometry.h:36
Definition geometry.h:84
scalar blur_radius
Definition skia_shadow.h:182
scalar umbra_inset
Definition skia_shadow.h:180
bool is_circle
Definition skia_shadow.h:185
RRectType type
Definition skia_shadow.h:184
QRectF dev_bounds
Definition skia_shadow.h:183
scalar inner_radius
Definition skia_shadow.h:181
QRgb color
Definition skia_shadow.h:178
scalar outer_radius
Definition skia_shadow.h:179