summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenjamin Otte <otte@redhat.com>2023-05-07 01:49:29 +0200
committerBenjamin Otte <otte@redhat.com>2023-05-17 02:25:32 +0200
commit17ab6203520a8cfde318e2fa28b96ce3e76e874b (patch)
treee60698586c0a37af55b8940c3c3ce376bc746cd6
parent53c04ca39ce4dc5f34f8d84ffccbf3f839cff830 (diff)
downloadgtk+-17ab6203520a8cfde318e2fa28b96ce3e76e874b.tar.gz
vulkan: Rewrite rounded rectangle to use SDF distance
We can use this to properly compute distance in scaled situations. We also now compute coverage with (imperfect) antialiasing.
-rw-r--r--gsk/vulkan/resources/clip.frag.glsl2
-rw-r--r--gsk/vulkan/resources/ellipse.glsl38
-rw-r--r--gsk/vulkan/resources/meson.build1
-rw-r--r--gsk/vulkan/resources/rounded-rect.frag.glsl17
-rw-r--r--gsk/vulkan/resources/rounded-rect.glsl70
5 files changed, 85 insertions, 43 deletions
diff --git a/gsk/vulkan/resources/clip.frag.glsl b/gsk/vulkan/resources/clip.frag.glsl
index 472dc6f58c..4f4755c5bb 100644
--- a/gsk/vulkan/resources/clip.frag.glsl
+++ b/gsk/vulkan/resources/clip.frag.glsl
@@ -1,5 +1,5 @@
#include "constants.glsl"
-#include "rounded-rect.glsl"
+#include "rounded-rect.frag.glsl"
#ifndef _CLIP_
#define _CLIP_
diff --git a/gsk/vulkan/resources/ellipse.glsl b/gsk/vulkan/resources/ellipse.glsl
new file mode 100644
index 0000000000..08986de731
--- /dev/null
+++ b/gsk/vulkan/resources/ellipse.glsl
@@ -0,0 +1,38 @@
+#ifndef _ELLIPSE_
+#define _ELLIPSE_
+
+struct Ellipse
+{
+ vec2 center;
+ vec2 radius;
+};
+
+float
+ellipse_distance (Ellipse r, vec2 p)
+{
+ vec2 e = r.radius;
+ p = p - r.center;
+
+ if (e.x == e.y)
+ return length (p) - e.x;
+
+ /* from https://www.shadertoy.com/view/tt3yz7 */
+ vec2 pAbs = abs(p);
+ vec2 ei = 1.0 / e;
+ vec2 e2 = e*e;
+ vec2 ve = ei * vec2(e2.x - e2.y, e2.y - e2.x);
+
+ vec2 t = vec2(0.70710678118654752, 0.70710678118654752);
+ for (int i = 0; i < 3; i++) {
+ vec2 v = ve*t*t*t;
+ vec2 u = normalize(pAbs - v) * length(t * e - v);
+ vec2 w = ei * (v + u);
+ t = normalize(clamp(w, 0.0, 1.0));
+ }
+
+ vec2 nearestAbs = t * e;
+ float dist = length(pAbs - nearestAbs);
+ return dot(pAbs, pAbs) < dot(nearestAbs, nearestAbs) ? -dist : dist;
+}
+
+#endif
diff --git a/gsk/vulkan/resources/meson.build b/gsk/vulkan/resources/meson.build
index 802f391b4d..2bcbd03ba3 100644
--- a/gsk/vulkan/resources/meson.build
+++ b/gsk/vulkan/resources/meson.build
@@ -6,6 +6,7 @@ gsk_private_vulkan_include_shaders = [
'rect.frag.glsl',
'rect.vert.glsl',
'rounded-rect.glsl',
+ 'rounded-rect.frag.glsl',
]
gsk_private_vulkan_fragment_shaders = [
diff --git a/gsk/vulkan/resources/rounded-rect.frag.glsl b/gsk/vulkan/resources/rounded-rect.frag.glsl
new file mode 100644
index 0000000000..fe62dab565
--- /dev/null
+++ b/gsk/vulkan/resources/rounded-rect.frag.glsl
@@ -0,0 +1,17 @@
+#ifndef _ROUNDED_RECT_FRAG_
+#define _ROUNDED_RECT_FRAG_
+
+#include "rounded-rect.glsl"
+
+float
+rounded_rect_coverage (RoundedRect r, vec2 p)
+{
+ vec2 fw = abs (fwidth (p));
+ float distance_scale = max (fw.x, fw.y);
+ float distance = rounded_rect_distance (r, p) / distance_scale;
+ float coverage = 0.5 - distance;
+
+ return clamp (coverage, 0, 1);
+}
+
+#endif
diff --git a/gsk/vulkan/resources/rounded-rect.glsl b/gsk/vulkan/resources/rounded-rect.glsl
index e0c2ffc6f9..5ee5dfd2fa 100644
--- a/gsk/vulkan/resources/rounded-rect.glsl
+++ b/gsk/vulkan/resources/rounded-rect.glsl
@@ -1,6 +1,9 @@
#ifndef _ROUNDED_RECT_
#define _ROUNDED_RECT_
+#include "ellipse.glsl"
+#include "rect.glsl"
+
struct RoundedRect
{
vec4 bounds;
@@ -9,51 +12,34 @@ struct RoundedRect
};
float
-ellipsis_dist (vec2 p, vec2 radius)
+rounded_rect_distance (RoundedRect r, vec2 p)
{
- vec2 p0 = p / radius;
- vec2 p1 = 2.0 * p0 / radius;
-
- return (dot(p0, p0) - 1.0) / length (p1);
-}
-
-float
-ellipsis_coverage (vec2 point, vec2 center, vec2 radius)
-{
- float d = ellipsis_dist (point - center, radius);
- return clamp (0.5 - d, 0.0, 1.0);
-}
+ Rect bounds = Rect(vec4(r.bounds));
-float
-rounded_rect_coverage (RoundedRect r, vec2 p)
-{
- if (p.x < r.bounds.x || p.y < r.bounds.y ||
- p.x >= r.bounds.z || p.y >= r.bounds.w)
- return 0.0;
+ float bounds_distance = rect_distance (bounds, p);
- vec2 rad_tl = vec2(r.corner_widths.x, r.corner_heights.x);
- vec2 rad_tr = vec2(r.corner_widths.y, r.corner_heights.y);
- vec2 rad_br = vec2(r.corner_widths.z, r.corner_heights.z);
- vec2 rad_bl = vec2(r.corner_widths.w, r.corner_heights.w);
-
- vec2 ref_tl = r.bounds.xy + vec2( r.corner_widths.x, r.corner_heights.x);
- vec2 ref_tr = r.bounds.zy + vec2(-r.corner_widths.y, r.corner_heights.y);
- vec2 ref_br = r.bounds.zw + vec2(-r.corner_widths.z, -r.corner_heights.z);
- vec2 ref_bl = r.bounds.xw + vec2( r.corner_widths.w, -r.corner_heights.w);
-
- float d_tl = ellipsis_coverage(p, ref_tl, rad_tl);
- float d_tr = ellipsis_coverage(p, ref_tr, rad_tr);
- float d_br = ellipsis_coverage(p, ref_br, rad_br);
- float d_bl = ellipsis_coverage(p, ref_bl, rad_bl);
-
- vec4 corner_coverages = 1.0 - vec4(d_tl, d_tr, d_br, d_bl);
-
- bvec4 is_out = bvec4(p.x < ref_tl.x && p.y < ref_tl.y,
- p.x > ref_tr.x && p.y < ref_tr.y,
- p.x > ref_br.x && p.y > ref_br.y,
- p.x < ref_bl.x && p.y > ref_bl.y);
-
- return 1.0 - dot(vec4(is_out), corner_coverages);
+ Ellipse tl = Ellipse (r.bounds.xy + vec2( r.corner_widths.x, r.corner_heights.x),
+ vec2(r.corner_widths.x, r.corner_heights.x));
+ Ellipse tr = Ellipse (r.bounds.zy + vec2(-r.corner_widths.y, r.corner_heights.y),
+ vec2(r.corner_widths.y, r.corner_heights.y));
+ Ellipse br = Ellipse (r.bounds.zw + vec2(-r.corner_widths.z, -r.corner_heights.z),
+ vec2(r.corner_widths.z, r.corner_heights.z));
+ Ellipse bl = Ellipse (r.bounds.xw + vec2( r.corner_widths.w, -r.corner_heights.w),
+ vec2(r.corner_widths.w, r.corner_heights.w));
+
+ vec4 distances = vec4(ellipse_distance (tl, p),
+ ellipse_distance (tr, p),
+ ellipse_distance (br, p),
+ ellipse_distance (bl, p));
+
+ bvec4 is_out = bvec4(p.x < tl.center.x && p.y < tl.center.y,
+ p.x > tr.center.x && p.y < tr.center.y,
+ p.x > br.center.x && p.y > br.center.y,
+ p.x < bl.center.x && p.y > bl.center.y);
+ distances = mix (vec4(bounds_distance), distances, is_out);
+
+ vec2 max2 = max (distances.xy, distances.zw);
+ return max (max2.x, max2.y);
}
RoundedRect