Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git
scenefx/render/fx_renderer/shaders/corner_alpha.frag (3.4K)
1 // Corner-shape exponent: 2 = circular arc, > 2 = superellipse "squircle"
2 // corners — kept in lockstep with the cce-ui clients' corner_shape so the
3 // compositor's corner cut lands exactly on the corners the clients draw.
4 uniform float corner_shape;
5
6 float get_dist(vec2 q, float radius) {
7 vec2 p = max(q, 0.0);
8 if (corner_shape > 2.001 && radius > 0.0 && p.x > 0.0 && p.y > 0.0) {
9 // Superellipse (Lp-norm) corner, radius-normalized so the pow()
10 // arguments stay near 1 (mediump-safe). The Lp gradient is not unit
11 // length, so the distance carries a first-order |grad| correction —
12 // exact on the boundary, where the AA smoothstep samples it.
13 vec2 u = p / radius;
14 float lp = max(pow(pow(u.x, corner_shape) + pow(u.y, corner_shape),
15 1.0 / corner_shape), 1e-4);
16 vec2 g = vec2(pow(u.x / lp, corner_shape - 1.0),
17 pow(u.y / lp, corner_shape - 1.0));
18 return min(max(q.x, q.y), 0.0)
19 + radius * (lp - 1.0) / max(length(g), 1e-4);
20 }
21 return min(max(q.x, q.y), 0.0) + length(p) - radius;
22 }
23
24 // Note: Returns 0.0 if outside, 1.0 if inside the bounds. The is_cutout parameter
25 // reverses this, 0.0 inside cutout and 1.0 for outside cutout.
26 float corner_alpha(vec2 size, vec2 position, bool is_cutout,
27 float radius_tl, float radius_tr, float radius_bl, float radius_br) {
28 if (radius_tl <= 0.0
29 && radius_tr <= 0.0
30 && radius_bl <= 0.0
31 && radius_br <= 0.0) {
32 return 1.0;
33 }
34
35 vec2 relative_pos = (gl_FragCoord.xy - position);
36 relative_pos.y = size.y - relative_pos.y;
37
38 if (relative_pos.x < -0.5 || relative_pos.y < -0.5
39 || relative_pos.x > size.x + 0.5 || relative_pos.y > size.y + 0.5) {
40 if (is_cutout) {
41 return 1.0;
42 }
43 discard;
44 }
45
46 bool is_top_left = radius_tl > 0.0
47 && relative_pos.x <= radius_tl
48 && relative_pos.y <= radius_tl;
49 bool is_top_right = radius_tr > 0.0
50 && relative_pos.x >= size.x - radius_tr
51 && relative_pos.y <= radius_tr;
52 bool is_bottom_left = radius_bl > 0.0
53 && relative_pos.x <= radius_bl
54 && relative_pos.y >= size.y - radius_bl;
55 bool is_bottom_right = radius_br > 0.0
56 && relative_pos.x >= size.x - radius_br
57 && relative_pos.y >= size.y - radius_br;
58 if (!is_top_left && !is_top_right && !is_bottom_left && !is_bottom_right) {
59 if (is_cutout) {
60 discard;
61 }
62 return 1.0;
63 }
64
65 vec2 top_left = abs(relative_pos - size) - size + radius_tl;
66 vec2 top_right = abs(relative_pos - vec2(0, size.y)) - size + radius_tr;
67 vec2 bottom_left = abs(relative_pos - vec2(size.x, 0)) - size + radius_bl;
68 vec2 bottom_right = abs(relative_pos) - size + radius_br;
69
70 float dist = max(
71 max(get_dist(top_left, radius_tl), get_dist(top_right, radius_tr)),
72 max(get_dist(bottom_left, radius_bl), get_dist(bottom_right, radius_br))
73 );
74
75 float result = smoothstep(0.0, 1.0, dist);
76 return is_cutout ? result : 1.0 - result;
77 }
78
79 float corner_dist(vec2 size, vec2 position,
80 float radius_tl, float radius_tr, float radius_bl, float radius_br) {
81 vec2 relative_pos = (gl_FragCoord.xy - position);
82 relative_pos.y = size.y - relative_pos.y;
83
84 vec2 top_left = abs(relative_pos - size) - size + radius_tl;
85 vec2 top_right = abs(relative_pos - vec2(0, size.y)) - size + radius_tr;
86 vec2 bottom_left = abs(relative_pos - vec2(size.x, 0)) - size + radius_bl;
87 vec2 bottom_right = abs(relative_pos) - size + radius_br;
88
89 float dist = max(
90 max(get_dist(top_left, radius_tl), get_dist(top_right, radius_tr)),
91 max(get_dist(bottom_left, radius_bl), get_dist(bottom_right, radius_br))
92 );
93
94 return dist;
95 }