Files
z-bar-qt/Plugins/ZShell/Blobs/shaders/blob.frag
T
zach 11e7aac195
C++ / fmt (pull_request) Failing after 5s
C++ / build (pull_request) Failing after 11s
C++ / clang-tidy (pull_request) Failing after 10s
JS/TS / fmt (pull_request) Successful in 12s
JS/TS / lint (pull_request) Successful in 10s
Python / static (pull_request) Failing after 27s
Rust / build (pull_request) Successful in 54s
Python / verify (pull_request) Failing after 1m26s
Rust / fmt (pull_request) Successful in 46s
Rust / clippy (pull_request) Successful in 1m6s
feat: better notification storage and image caching + blobs
2026-11-02 19:00:43 +01:00

270 lines
9.7 KiB
GLSL

#version 440
layout(location = 0) in vec2 qt_TexCoord0;
layout(location = 0) out vec4 fragColor;
layout(std140, binding = 0) uniform buf {
mat4 qt_Matrix;
float qt_Opacity;
float paddedX;
float paddedY;
float paddedW;
float paddedH;
float smoothFactor;
int rectCount;
int myIndex;
vec4 color;
int hasInverted;
float invertedRadius;
vec4 invertedOuter;
vec4 invertedInner;
vec4 rectData[80];
float shadowRadius;
vec4 shadowColor;
vec4 invertedOuterRadii;
};
float sdRoundedBox(vec2 p, vec2 center, vec2 halfSize, float radius) {
vec2 d = abs(p - center) - halfSize + vec2(radius);
return length(max(d, vec2(0.0))) + min(max(d.x, d.y), 0.0) - radius;
}
float sdRoundedBox4(vec2 p, vec2 center, vec2 halfSize, vec4 r) {
p -= center;
r.xy = (p.x > 0.0) ? r.xy : r.wz;
r.x = (p.y > 0.0) ? r.y : r.x;
vec2 q = abs(p) - halfSize + r.x;
return min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - r.x;
}
float sdBox(vec2 p, vec2 center, vec2 halfSize) {
vec2 d = abs(p - center) - halfSize;
return length(max(d, vec2(0.0))) + min(max(d.x, d.y), 0.0);
}
float smin(float a, float b, float k) {
// Circular smooth min — the blend fillet is a true circular arc of radius k,
// tangent to both surfaces (not a polynomial/squircle curve). Deviates from
// min(a, b) only in the corner region where BOTH a < k and b < k (unlike the
// cubic, which deviates over the whole band |a - b| < k and dents the surface
// inward while the shapes are still apart). Always <= min(a, b); max blend
// depth at a == b is (sqrt(2) - 1) * k.
return max(k, min(a, b)) - length(max(vec2(k) - vec2(a, b), vec2(0.0)));
}
float smax(float a, float b, float k) {
// Circular smooth max — dual of smin: -smin(-a, -b, k). Always >= max(a, b).
return min(-k, max(a, b)) + length(max(vec2(a, b) + vec2(k), vec2(0.0)));
}
float smaxSharpA(float a, float b, float k) {
// Circular smax variant that keeps a's boundary sharp (no inward rounding at
// a = 0). Used for the frame outer edge so it always fills to the edges.
float sm =
min(-k, max(a, b)) + length(max(vec2(a, b) + vec2(k), vec2(0.0)));
float blend = (sm - max(a, b)) * smoothstep(0.0, k * 0.5, -a);
return max(a, b) + blend;
}
void main() {
vec2 pixel = vec2(paddedX, paddedY) + qt_TexCoord0 * vec2(paddedW, paddedH);
float dArr[16];
int owner = -2;
float minDist = 1e10;
for (int i = 0; i < rectCount; i++) {
vec4 rect = rectData[i * 5]; // cx, cy, hw, hh
vec4 props =
rectData[i * 5 + 1]; // excludeMask(int bits), offsetX, offsetY, minEig
vec4 invDm = rectData[i * 5 + 2]; // inverse deform matrix
vec4 sh = rectData[i * 5 + 3]; // screenHalfX, screenHalfY, 0, 0
vec4 radii =
rectData[i * 5 + 4]; // effective per-corner radii (tr, br, bl, tl)
vec2 center = rect.xy + props.yz;
vec2 extent = sh.xy + vec2(smoothFactor * 1.5 + shadowRadius);
if (abs(pixel.x - center.x) > extent.x ||
abs(pixel.y - center.y) > extent.y) {
dArr[i] = 1e10;
continue;
}
mat2 invDeform = mat2(invDm.xy, invDm.zw);
vec2 transformedPixel = center + invDeform * (pixel - center);
float d = sdRoundedBox4(transformedPixel, center, rect.zw, radii);
d *= max(props.w, 0.01);
if (hasInverted != 0) {
vec2 screenHalf = sh.xy;
float distY0 =
(center.y + screenHalf.y) - (invertedInner.y - invertedInner.w);
float distY1 =
(invertedInner.y + invertedInner.w) - (center.y - screenHalf.y);
float distX0 =
(center.x + screenHalf.x) - (invertedInner.x - invertedInner.z);
float distX1 =
(invertedInner.x + invertedInner.z) - (center.x - screenHalf.x);
float yProx = 1.0 - min(smoothstep(0.0, smoothFactor, distY0),
smoothstep(0.0, smoothFactor, distY1));
float xProx = 1.0 - min(smoothstep(0.0, smoothFactor, distX0),
smoothstep(0.0, smoothFactor, distX1));
vec2 q = abs(pixel - center) - screenHalf;
vec2 qp = max(q, vec2(0.0));
float cornerLen = length(qp);
float gradX = qp.x / max(cornerLen, 0.001);
float gradY = qp.y / max(cornerLen, 0.001);
float faceY = smoothstep(-4.0, 4.0, q.y - q.x);
float faceX = 1.0 - faceY;
float t = smoothstep(0.0, 2.0, cornerLen);
float xWeight = mix(faceX, gradX, t);
float yWeight = mix(faceY, gradY, t);
float boost = 3.0;
float scale = 1.0 + (xProx * xWeight + yProx * yWeight) * boost;
d *= scale;
}
dArr[i] = d;
if (d < smoothFactor && d < minDist) {
minDist = d;
owner = i;
}
}
float mergedSdf = 1e10;
for (int i = 0; i < rectCount; i++) {
mergedSdf = min(mergedSdf, dArr[i]);
}
for (int i = 0; i < rectCount; i++) {
if (dArr[i] >= 1e9)
continue;
int excludeMask = floatBitsToInt(rectData[i * 5 + 1].x);
for (int j = i + 1; j < rectCount; j++) {
if (dArr[j] >= 1e9)
continue;
if ((excludeMask & (1 << j)) != 0)
continue;
// Circular smin deviates from min only where BOTH d values are < k.
if (max(dArr[i], dArr[j]) >= smoothFactor)
continue;
mergedSdf = min(mergedSdf, smin(dArr[i], dArr[j], smoothFactor));
}
}
if (hasInverted != 0) {
float dOuter =
sdRoundedBox4(pixel, invertedOuter.xy, invertedOuter.zw,
invertedOuterRadii) - 1.0;
float dInner =
sdRoundedBox(pixel, invertedInner.xy, invertedInner.zw, invertedRadius);
float innerTop = invertedInner.y - invertedInner.w;
float innerBot = invertedInner.y + invertedInner.w;
float innerLeft = invertedInner.x - invertedInner.z;
float innerRight = invertedInner.x + invertedInner.z;
float outerTop = invertedOuter.y - invertedOuter.w;
float outerBot = invertedOuter.y + invertedOuter.w;
float outerLeft = invertedOuter.x - invertedOuter.z;
float outerRight = invertedOuter.x + invertedOuter.z;
float topZone =
1.0 - smoothstep(innerTop, innerTop + smoothFactor, pixel.y);
float botZone = smoothstep(innerBot - smoothFactor, innerBot, pixel.y);
float leftZone =
1.0 - smoothstep(innerLeft, innerLeft + smoothFactor, pixel.x);
float rightZone =
smoothstep(innerRight - smoothFactor, innerRight, pixel.x);
float sinkValue = 0.0;
// Sink onset / residual overlap: how far a rect must penetrate the border
// before the inner wall recedes to form its pocket. Too low and the wall
// recedes faster than the junction can stay convex, denting the inner edge
// inward near the rect's corners; too high and the rect nestles too deep
// before the wall yields. Tuned to the circular smin blend depth: half the
// gap-closing distance, (2 - sqrt(2)) * k / 2.
float preOff = smoothFactor * (2.0 - sqrt(2.0)) * 0.5;
float s = smoothFactor * 2.0;
if (topZone > 0.0 || botZone > 0.0 || leftZone > 0.0 || rightZone > 0.0) {
for (int i = 0; i < rectCount; i++) {
vec4 rect = rectData[i * 5];
vec4 sinkProps = rectData[i * 5 + 1];
vec2 sinkSh = rectData[i * 5 + 3].xy;
vec2 ctr = rect.xy + sinkProps.yz;
float topPen = clamp(innerTop - (ctr.y + sinkSh.y) - preOff, 0.0,
innerTop - outerTop);
float botPen = clamp((ctr.y - sinkSh.y) - innerBot - preOff, 0.0,
outerBot - innerBot);
float leftPen = clamp(innerLeft - (ctr.x + sinkSh.x) - preOff, 0.0,
innerLeft - outerLeft);
float rightPen = clamp((ctr.x - sinkSh.x) - innerRight - preOff, 0.0,
outerRight - innerRight);
float hLat = max(abs(pixel.x - ctr.x) - sinkSh.x, 0.0);
float vLat = max(abs(pixel.y - ctr.y) - sinkSh.y, 0.0);
float sink = max(max(topPen * smoothstep(s, 0.0, hLat) * topZone,
botPen * smoothstep(s, 0.0, hLat) * botZone),
max(leftPen * smoothstep(s, 0.0, vLat) * leftZone,
rightPen * smoothstep(s, 0.0, vLat) * rightZone));
sinkValue = max(sinkValue, sink);
}
}
dInner -= sinkValue;
// The circular smax fillet has radius kFrame; when it exceeds the border
// thickness it can't complete inside the border, so the sharp outer-box term
// bleeds onto the inner edge and bulges the inner corners (worst when
// thickness < smoothFactor). Clamp kFrame to the thinnest side so the inner
// edge stays a clean constant-radius arc. Each inner corner is bounded by
// its thinner adjacent side, so the global min is correct for every corner.
float minThick = min(min(innerTop - outerTop, outerBot - innerBot),
min(innerLeft - outerLeft, outerRight - innerRight));
float kFrame = clamp(min(smoothFactor, minThick - 1.0), 1.0, smoothFactor);
float dFrame = smaxSharpA(dOuter, -dInner, kFrame);
mergedSdf = smin(mergedSdf, dFrame, smoothFactor);
if (dFrame < minDist) {
owner = -1;
}
}
float reach = max(smoothFactor, shadowRadius);
float pxWidth = max(length(vec2(dFdx(mergedSdf), dFdy(mergedSdf))), 1e-5);
if (mergedSdf > reach - max(2.0 * pxWidth, 1.0))
discard;
if (myIndex >= -1 && owner != myIndex)
discard;
float materialAlpha = clamp(0.5 - mergedSdf / pxWidth, 0.0, 1.0);
float shadowAlpha = 0.0;
if (shadowRadius > 0.0 && myIndex == -2) {
float t = clamp(mergedSdf / shadowRadius, 0.0, 1.0);
shadowAlpha = (1.0 - t) * (1.0 - t) * shadowColor.a;
}
float totalAlpha = materialAlpha + shadowAlpha * (1.0 - materialAlpha);
vec3 rgb = color.rgb * materialAlpha
+ shadowColor.rgb * shadowAlpha * (1.0 - materialAlpha);
fragColor = vec4(rgb, totalAlpha) * qt_Opacity;
}