2#include "qml_material/scenegraph/geometry.h"
6namespace qml_material::sk
12static constexpr const T& SkTPin(
const T& x,
const T& lo,
const T& hi) {
13 return std::max(lo, std::min(x, hi));
16constexpr auto sk_double_round(
double x) {
return std::floor((x) + 0.5); }
17constexpr auto sk_float_round(
double x) {
return (
float)std::floor((x) + 0.5); }
19template<
typename T, std::enable_if_t<std::is_
floating_po
int_v<T>,
bool> = true>
20static inline constexpr bool SkIsNaN(T x) {
24#if defined(_MSC_VER) && ! defined(__clang__)
25# define SK_CHECK_NAN(resultVal) \
30# define SK_CHECK_NAN(resultVal)
33inline constexpr int SK_MaxS32FitsInFloat = 2147483520;
34inline constexpr int SK_MinS32FitsInFloat = -SK_MaxS32FitsInFloat;
35static constexpr int sk_float_saturate2int(
float x) {
36 SK_CHECK_NAN(SK_MaxS32FitsInFloat)
37 x = x < SK_MaxS32FitsInFloat ? x : SK_MaxS32FitsInFloat;
38 x = x > SK_MinS32FitsInFloat ? x : SK_MinS32FitsInFloat;
41constexpr auto SkScalarRoundToInt(
double x) {
return sk_float_saturate2int(sk_float_round(x)); }
43static constexpr auto kAmbientHeightFactor = 1.0f / 128.0f;
44static constexpr auto kAmbientGeomFactor = 64.0f;
48static constexpr auto kMaxAmbientRadius = 300 * kAmbientHeightFactor * kAmbientGeomFactor;
50static inline float divide_and_pin(
float numer,
float denom,
float min,
float max) {
51 float result = SkTPin(numer / denom, min, max);
53 Q_ASSERT(result >= min && result <= max);
56constexpr auto SK_Scalar1 { 1.0f };
57constexpr auto SK_ScalarNearlyZero { (SK_Scalar1 / (1 << 12)) };
59inline scalar AmbientBlurRadius(scalar height) {
60 return std::min(height * kAmbientHeightFactor * kAmbientGeomFactor, kMaxAmbientRadius);
62inline scalar AmbientRecipAlpha(scalar height) {
63 return 1.0f + std::max(height * kAmbientHeightFactor, 0.0f);
66inline scalar SpotBlurRadius(scalar occluderZ, scalar lightZ, scalar lightRadius) {
67 return lightRadius * divide_and_pin(occluderZ, lightZ - occluderZ, 0.0f, 0.95f);
70inline void GetSpotParams(scalar occluderZ, scalar lightX, scalar lightY, scalar lightZ,
71 scalar lightRadius, scalar* blurRadius, scalar* scale,
72 QVector2D* translate) {
73 scalar zRatio = divide_and_pin(occluderZ, lightZ - occluderZ, 0.0f, 0.95f);
74 *blurRadius = lightRadius * zRatio;
75 *scale = divide_and_pin(lightZ, lightZ - occluderZ, 1.0f, 1.95f);
76 *translate = QVector2D(-zRatio * lightX, -zRatio * lightY);
79inline void GetDirectionalParams(scalar occluderZ, scalar lightX, scalar lightY, scalar lightZ,
80 scalar lightRadius, scalar* blurRadius, scalar* scale,
81 QVector2D* translate) {
82 *blurRadius = lightRadius * occluderZ;
85 constexpr scalar kMaxZRatio = 64 / SK_ScalarNearlyZero;
86 scalar zRatio = divide_and_pin(occluderZ, lightZ, 0.0f, kMaxZRatio);
87 *translate = QVector2D(-zRatio * lightX, -zRatio * lightY);
90class ShadowCircularRRectOp {
97 kOverstroke_RRectType,
100 ShadowCircularRRectOp(QRgb color,
const QRectF& devRect,
float devRadius,
bool isCircle,
101 float blurRadius,
float insetWidth)
102 : fIndexPtr(nullptr) {
103 QRectF bounds = devRect;
104 Q_ASSERT(insetWidth > 0);
105 scalar innerRadius = 0.0f;
106 scalar outerRadius = devRadius;
109 RRectType type = kFill_RRectType;
113 umbraInset = std::max(outerRadius, blurRadius);
119 innerRadius = devRadius - insetWidth;
120 type = innerRadius > 0 ? kStroke_RRectType : kFill_RRectType;
122 if (insetWidth <= 0.5f * std::min(devRect.width(), devRect.height())) {
125 innerRadius = std::max(insetWidth - umbraInset, 0.0f);
126 type = innerRadius > 0 ? kOverstroke_RRectType : kStroke_RRectType;
132 fGeoData = (Geometry {
133 color, outerRadius, umbraInset, innerRadius, blurRadius, bounds, type, isCircle });
135 fVertCount = circle_type_to_vert_count(kStroke_RRectType == type);
136 fIndexCount = circle_type_to_index_count(kStroke_RRectType == type);
137 fIndexPtr = circle_type_to_indices(kStroke_RRectType == type);
139 fVertCount = rrect_type_to_vert_count(type);
140 fIndexCount = rrect_type_to_index_count(type);
141 fIndexPtr = rrect_type_to_indices(type);
156 static constexpr uint16_t gFillCircleIndices[] = {
167 static constexpr uint16_t gStrokeCircleIndices[] = {
181 static constexpr uint16_t gRRectIndices[] = {
186 6, 18, 27, 6, 27, 25,
187 18, 12, 26, 18, 26, 27,
188 12, 0, 24, 12, 24, 26,
191 0, 1, 2, 0, 2, 3, 0, 3, 4, 0, 4, 5,
192 6, 11, 10, 6, 10, 9, 6, 9, 8, 6, 8, 7,
193 12, 17, 16, 12, 16, 15, 12, 15, 14, 12, 14, 13,
194 18, 19, 20, 18, 20, 21, 18, 21, 22, 18, 22, 23,
199 18, 23, 17, 18, 17, 12,
208 static constexpr float SK_FloatSqrt2 = 1.41421356f;
211 static constexpr int kIndicesPerOverstrokeRRect = std::size(gRRectIndices) - 6;
213 static constexpr int kIndicesPerStrokeRRect = kIndicesPerOverstrokeRRect - 6 * 4;
215 static constexpr int kIndicesPerFillRRect = kIndicesPerStrokeRRect + 6;
216 static constexpr int kVertsPerStrokeRRect = 24;
217 static constexpr int kVertsPerOverstrokeRRect = 28;
218 static constexpr int kVertsPerFillRRect = 24;
220 static constexpr int kIndicesPerFillCircle = std::size(gFillCircleIndices);
221 static constexpr int kIndicesPerStrokeCircle = std::size(gStrokeCircleIndices);
222 static constexpr int kVertsPerStrokeCircle = 16;
223 static constexpr int kVertsPerFillCircle = 9;
225 static int circle_type_to_vert_count(
bool stroked) {
226 return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle;
229 static int circle_type_to_index_count(
bool stroked) {
230 return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle;
233 static const uint16_t* circle_type_to_indices(
bool stroked) {
234 return stroked ? gStrokeCircleIndices : gFillCircleIndices;
237 static int rrect_type_to_vert_count(RRectType type) {
239 case kFill_RRectType:
return kVertsPerFillRRect;
240 case kStroke_RRectType:
return kVertsPerStrokeRRect;
241 case kOverstroke_RRectType:
return kVertsPerOverstrokeRRect;
246 static int rrect_type_to_index_count(RRectType type) {
248 case kFill_RRectType:
return kIndicesPerFillRRect;
249 case kStroke_RRectType:
return kIndicesPerStrokeRRect;
250 case kOverstroke_RRectType:
return kIndicesPerOverstrokeRRect;
255 static const uint16_t* rrect_type_to_indices(RRectType type) {
257 case kFill_RRectType:
258 case kStroke_RRectType:
return gRRectIndices + 6 * 4;
259 case kOverstroke_RRectType:
return gRRectIndices;
265 void fillInCircleVerts(
bool isStroked, sg::ShadowVertex** vp)
const {
266 const auto& args = this->fGeoData;
267 QRgb color = args.color;
268 scalar outerRadius = args.outer_radius;
269 scalar innerRadius = args.inner_radius;
270 scalar blurRadius = args.blur_radius;
271 scalar distanceCorrection = outerRadius / blurRadius;
273 const QRectF& bounds = args.dev_bounds;
276 innerRadius = innerRadius / outerRadius;
278 auto center = QVector2D(bounds.center().x(), bounds.center().y());
279 scalar halfWidth = 0.5f * bounds.width();
280 scalar octOffset = 0.41421356237f;
284 v->set_point(center + QVector2D(-octOffset * halfWidth, -halfWidth));
286 v->set_offset({ -octOffset, -1 });
287 v->distance_correction = distanceCorrection;
291 v->set_point(center + QVector2D(octOffset * halfWidth, -halfWidth));
293 v->set_offset({ octOffset, -1 });
294 v->distance_correction = distanceCorrection;
298 v->set_point(center + QVector2D(halfWidth, -octOffset * halfWidth));
300 v->set_offset({ 1, -octOffset });
301 v->distance_correction = distanceCorrection;
305 v->set_point(center + QVector2D(halfWidth, octOffset * halfWidth));
307 v->set_offset({ 1, octOffset });
308 v->distance_correction = distanceCorrection;
312 v->set_point(center + QVector2D(octOffset * halfWidth, halfWidth));
314 v->set_offset({ octOffset, 1 });
315 v->distance_correction = distanceCorrection;
319 v->set_point(center + QVector2D(-octOffset * halfWidth, halfWidth));
321 v->set_offset({ -octOffset, 1 });
322 v->distance_correction = distanceCorrection;
326 v->set_point(center + QVector2D(-halfWidth, octOffset * halfWidth));
328 v->set_offset({ -1, octOffset });
329 v->distance_correction = distanceCorrection;
333 v->set_point(center + QVector2D(-halfWidth, -octOffset * halfWidth));
335 v->set_offset({ -1, -octOffset });
336 v->distance_correction = distanceCorrection;
342 scalar c = 0.923579533f;
343 scalar s = 0.382683432f;
344 scalar r = args.inner_radius;
345 v->set_point(center + QVector2D(-s * r, -c * r));
347 v->set_offset({ -s * innerRadius, -c * innerRadius });
348 v->distance_correction = distanceCorrection;
352 v->set_point(center + QVector2D(s * r, -c * r));
354 v->set_offset({ s * innerRadius, -c * innerRadius });
355 v->distance_correction = distanceCorrection;
359 v->set_point(center + QVector2D(c * r, -s * r));
361 v->set_offset({ c * innerRadius, -s * innerRadius });
362 v->distance_correction = distanceCorrection;
366 v->set_point(center + QVector2D(c * r, s * r));
368 v->set_offset({ c * innerRadius, s * innerRadius });
369 v->distance_correction = distanceCorrection;
373 v->set_point(center + QVector2D(s * r, c * r));
375 v->set_offset({ s * innerRadius, c * innerRadius });
376 v->distance_correction = distanceCorrection;
380 v->set_point(center + QVector2D(-s * r, c * r));
382 v->set_offset({ -s * innerRadius, c * innerRadius });
383 v->distance_correction = distanceCorrection;
387 v->set_point(center + QVector2D(-c * r, s * r));
389 v->set_offset({ -c * innerRadius, s * innerRadius });
390 v->distance_correction = distanceCorrection;
394 v->set_point(center + QVector2D(-c * r, -s * r));
396 v->set_offset({ -c * innerRadius, -s * innerRadius });
397 v->distance_correction = distanceCorrection;
401 v->set_point(center);
403 v->set_offset({ 0, 0 });
404 v->distance_correction = distanceCorrection;
410 void fillInRRectVerts(sg::ShadowVertex** vp)
const {
411 const Geometry& args = this->fGeoData;
412 QRgb color = args.color;
413 scalar outer_radius = args.outer_radius;
415 const QRectF& bounds = args.dev_bounds;
417 scalar umbra_inset = args.umbra_inset;
418 scalar min_dim = 0.5f * std::min(bounds.width(), bounds.height());
419 if (umbra_inset > min_dim) {
420 umbra_inset = min_dim;
423 scalar xInner[4] = { (float)bounds.left() + umbra_inset,
424 (float)bounds.right() - umbra_inset,
425 (float)bounds.left() + umbra_inset,
426 (float)bounds.right() - umbra_inset };
427 scalar xMid[4] = { (float)bounds.left() + outer_radius,
428 (float)bounds.right() - outer_radius,
429 (float)bounds.left() + outer_radius,
430 (float)bounds.right() - outer_radius };
432 (float)bounds.left(), (float)bounds.right(), (float)bounds.left(), (float)bounds.right()
434 scalar yInner[4] = { (float)bounds.top() + umbra_inset,
435 (float)bounds.top() + umbra_inset,
436 (float)bounds.bottom() - umbra_inset,
437 (float)bounds.bottom() - umbra_inset };
438 scalar yMid[4] = { (float)bounds.top() + outer_radius,
439 (float)bounds.top() + outer_radius,
440 (float)bounds.bottom() - outer_radius,
441 (float)bounds.bottom() - outer_radius };
443 (float)bounds.top(), (float)bounds.top(), (float)bounds.bottom(), (float)bounds.bottom()
446 scalar blurRadius = args.blur_radius;
458 QVector2D outerVec = QVector2D(outer_radius - umbra_inset, -outer_radius - umbra_inset);
459 outerVec.normalize();
465 umbra_inset / (SK_FloatSqrt2 * (outer_radius - umbra_inset) - outer_radius);
466 QVector2D diag_vec = QVector2D(diag_val, diag_val);
467 scalar distance_correction = umbra_inset / blurRadius;
471 for (
int i = 0; i < 4; ++i) {
473 v->set_point(xInner[i], yInner[i]);
475 v->set_offset({ 0, 0 });
476 v->distance_correction = distance_correction;
480 v->set_point(xOuter[i], yInner[i]);
482 v->set_offset({ 0, -1 });
483 v->distance_correction = distance_correction;
486 v->set_point(xOuter[i], yMid[i]);
488 v->set_offset(outerVec);
489 v->distance_correction = distance_correction;
492 v->set_point(xOuter[i], yOuter[i]);
494 v->set_offset(diag_vec);
495 v->distance_correction = distance_correction;
498 v->set_point(xMid[i], yOuter[i]);
500 v->set_offset(outerVec);
501 v->distance_correction = distance_correction;
504 v->set_point(xInner[i], yOuter[i]);
506 v->set_offset({ 0, -1 });
507 v->distance_correction = distance_correction;
515 if (kOverstroke_RRectType == args.type) {
516 Q_ASSERT(args.inner_radius > 0.0f);
518 scalar inset = umbra_inset + args.inner_radius;
521 v->set_point(bounds.left() + inset, bounds.top() + inset);
523 v->set_offset({ 0, 0 });
524 v->distance_correction = distance_correction;
528 v->set_point(bounds.right() - inset, bounds.top() + inset);
530 v->set_offset({ 0, 0 });
531 v->distance_correction = distance_correction;
535 v->set_point(bounds.left() + inset, bounds.bottom() - inset);
537 v->set_offset({ 0, 0 });
538 v->distance_correction = distance_correction;
542 v->set_point(bounds.right() - inset, bounds.bottom() - inset);
544 v->set_offset({ 0, 0 });
545 v->distance_correction = distance_correction;
555 const uint16_t* fIndexPtr;