Glass cards scrolled out of view still ran their full effect path, and the rainbow border always used a segment count derived from a fixed per-pixel budget, so wide cards drew far more line segments than their slowly changing gradient needed. - A glass panel whose rectangle lies wholly outside the draw list's clip rect now returns early, so a scrolled-out card draws nothing (its border and ember passes are skipped along with the fill). - The rainbow border is split into segments by perimeter arc length: straight edges get one segment per ~32 dp, corner arcs get a few chords each to stay round, and a fully transparent border is skipped outright. A wide card now draws tens of segments instead of a couple hundred, with no visible change. Tests: the segment fractions are ascending and bounded, each corner arc keeps at least three chords, and the rect-cull margin test. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
47 lines
2.1 KiB
C++
47 lines
2.1 KiB
C++
#pragma once
|
|
#include <algorithm>
|
|
#include <cmath>
|
|
|
|
namespace dragonx { namespace ui { namespace effects {
|
|
|
|
/// Splits a rounded-rect border into line segments and writes the ascending perimeter fractions
|
|
/// out[0]=0 .. out[n]=1 (same parametrisation and piece order as ThemeEffects::perimeterPoint:
|
|
/// top, TR arc, right, BR arc, bottom, BL arc, left, TL arc). Straight edges get one segment per
|
|
/// `edgeStepPx` (<= 24 per edge); each corner arc gets clamp(ceil(r*0.4),3,8) chords, i.e. a
|
|
/// worst-case sag below 0.2 px. Returns n (>=1); needs maxSegs >= 128 + 1 entries in `out`.
|
|
inline int BorderSampleFractions(float w, float h, float r, float edgeStepPx,
|
|
float* out, int maxOut /* capacity of out, >= 129 */) {
|
|
constexpr float kPi = 3.14159265f;
|
|
out[0] = 0.0f;
|
|
if (w <= 0.0f || h <= 0.0f || maxOut < 129) { out[1] = 1.0f; return 1; }
|
|
r = std::min(r, std::min(w, h) * 0.5f);
|
|
if (r < 0.5f) r = 0.0f; // perimeterPoint treats tiny rounding as none
|
|
const float step = std::max(1.0f, edgeStepPx);
|
|
float len[8]; int segs[8]; int np;
|
|
auto edge = [&](float l) { return l <= 0.0f ? 0 : std::clamp((int)std::ceil(l / step), 1, 24); };
|
|
if (r <= 0.0f) {
|
|
np = 4;
|
|
len[0] = w; len[1] = h; len[2] = w; len[3] = h;
|
|
for (int i = 0; i < 4; ++i) segs[i] = edge(len[i]);
|
|
} else {
|
|
np = 8;
|
|
const float arc = kPi * r * 0.5f, ew = w - 2.0f * r, eh = h - 2.0f * r;
|
|
const int arcSegs = std::clamp((int)std::ceil(r * 0.4f), 3, 8);
|
|
const float L[8] = { ew, arc, eh, arc, ew, arc, eh, arc };
|
|
for (int i = 0; i < 8; ++i) { len[i] = L[i]; segs[i] = (i & 1) ? arcSegs : edge(L[i]); }
|
|
}
|
|
float perim = 0.0f; for (int i = 0; i < np; ++i) perim += len[i];
|
|
int n = 0; float d0 = 0.0f;
|
|
for (int i = 0; i < np; ++i) {
|
|
for (int s = 1; s <= segs[i]; ++s) {
|
|
out[++n] = (d0 + len[i] * (float)s / (float)segs[i]) / perim;
|
|
}
|
|
d0 += len[i];
|
|
}
|
|
if (n < 1) { out[1] = 1.0f; return 1; }
|
|
out[n] = 1.0f;
|
|
return n; // worst case 4*24 + 4*8 = 128
|
|
}
|
|
|
|
}}} // namespace
|