Skip to content

Commit 8b3c9fe

Browse files
committed
Rewrite RoundedCornersEffect
1 parent 81a3a00 commit 8b3c9fe

File tree

3 files changed

+175
-101
lines changed

3 files changed

+175
-101
lines changed

data/gala.gresource.xml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
<gresource prefix="/io/elementary/desktop/gala">
2424
<file compressed="true">shaders/colorblindness-correction.vert</file>
2525
<file compressed="true">shaders/monochrome.vert</file>
26-
<file compressed="true">shaders/rounded-corners.vert</file>
2726
</gresource>
2827
<gresource prefix="/io/elementary/desktop/gala-daemon">
2928
<file compressed="true">gala-daemon.css</file>

data/shaders/rounded-corners.vert

Lines changed: 0 additions & 59 deletions
This file was deleted.

lib/RoundedCornersEffect.vala

Lines changed: 175 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -3,48 +3,99 @@
33
* SPDX-License-Identifier: LGPL-3.0-or-later
44
*/
55

6-
public class Gala.RoundedCornersEffect : Clutter.ShaderEffect {
7-
private const int CLIP_RADIUS_OFFSET = 3;
6+
public class Gala.RoundedCornersEffect : Clutter.Effect {
7+
public float clip_radius { get; construct; }
8+
public float monitor_scale { get; construct set; }
89

9-
public float clip_radius {
10-
construct set {
11-
set_uniform_value ("clip_radius", value + CLIP_RADIUS_OFFSET);
12-
}
13-
}
14-
15-
private float _monitor_scale = 1.0f;
16-
public float monitor_scale {
17-
get {
18-
return _monitor_scale;
19-
}
20-
construct set {
21-
_monitor_scale = value;
10+
private Cogl.Framebuffer round_framebuffer;
11+
private Cogl.Pipeline round_pipeline;
12+
private Cogl.Texture round_texture;
13+
private int round_clip_radius_location;
14+
private int round_actor_size_location;
2215

23-
if (actor != null) {
24-
update_actor_size ();
25-
}
26-
}
16+
public RoundedCornersEffect (float clip_radius, float monitor_scale) {
17+
Object (clip_radius: clip_radius, monitor_scale: monitor_scale);
2718
}
2819

29-
public RoundedCornersEffect (float clip_radius, float monitor_scale) {
30-
Object (
31-
#if HAS_MUTTER48
32-
shader_type: Cogl.ShaderType.FRAGMENT,
20+
construct {
21+
#if HAS_MUTTER47
22+
unowned var ctx = actor.context.get_backend ().get_cogl_context ();
3323
#else
34-
shader_type: Clutter.ShaderType.FRAGMENT_SHADER,
24+
unowned var ctx = Clutter.get_default_backend ().get_cogl_context ();
3525
#endif
36-
clip_radius: clip_radius,
37-
monitor_scale: monitor_scale
26+
27+
round_pipeline = new Cogl.Pipeline (ctx);
28+
round_pipeline.set_layer_null_texture (0);
29+
round_pipeline.set_layer_filters (0, Cogl.PipelineFilter.LINEAR, Cogl.PipelineFilter.LINEAR);
30+
round_pipeline.set_layer_wrap_mode (0, Cogl.PipelineWrapMode.CLAMP_TO_EDGE);
31+
round_pipeline.add_snippet (
32+
new Cogl.Snippet (
33+
Cogl.SnippetHook.FRAGMENT,
34+
"""
35+
uniform sampler2D tex;
36+
uniform vec2 actor_size;
37+
uniform float clip_radius;
38+
39+
float rounded_rect_coverage (vec2 p) {
40+
float center_left = clip_radius;
41+
float center_right = actor_size.x - clip_radius;
42+
43+
float center_x;
44+
if (p.x < center_left) {
45+
center_x = center_left;
46+
} else if (p.x > center_right) {
47+
center_x = center_right;
48+
} else {
49+
return 1.0;
50+
}
51+
52+
float center_top = clip_radius;
53+
float center_bottom = actor_size.y - clip_radius;
54+
55+
float center_y;
56+
if (p.y < center_top) {
57+
center_y = center_top;
58+
} else if (p.y > center_bottom) {
59+
center_y = center_bottom;
60+
} else {
61+
return 1.0;
62+
}
63+
64+
vec2 delta = p - vec2 (center_x, center_y);
65+
float dist_squared = dot (delta, delta);
66+
67+
// Fully outside the circle
68+
float outer_radius = clip_radius + 0.5;
69+
if (dist_squared >= (outer_radius * outer_radius)) {
70+
return 0.0;
71+
}
72+
73+
// Fully inside the circle
74+
float inner_radius = clip_radius - 0.5;
75+
if (dist_squared <= (inner_radius * inner_radius)) {
76+
return 1.0;
77+
}
78+
// Only pixels on the edge of the curve need expensive antialiasing
79+
return smoothstep (outer_radius, inner_radius, sqrt (dist_squared));
80+
}
81+
""",
82+
83+
"""
84+
vec4 sample = texture2D (tex, cogl_tex_coord0_in.xy);
85+
vec2 texture_coord = cogl_tex_coord0_in.xy * actor_size;
86+
float res = rounded_rect_coverage (texture_coord);
87+
cogl_color_out = sample * cogl_color_in * res;
88+
"""
89+
)
3890
);
39-
}
4091

41-
construct {
42-
try {
43-
var bytes = GLib.resources_lookup_data ("/io/elementary/desktop/gala/shaders/rounded-corners.vert", GLib.ResourceLookupFlags.NONE);
44-
set_shader_source ((string) bytes.get_data ());
45-
} catch (Error e) {
46-
critical ("Unable to load rounded-corners.vert: %s", e.message);
47-
}
92+
round_clip_radius_location = round_pipeline.get_uniform_location ("clip_radius");
93+
round_actor_size_location = round_pipeline.get_uniform_location ("actor_size");
94+
95+
update_clip_radius ();
96+
update_actor_size ();
97+
98+
notify["monitor-scale"].connect (update_clip_radius);
4899
}
49100

50101
public override void set_actor (Clutter.Actor? new_actor) {
@@ -58,19 +109,102 @@ public class Gala.RoundedCornersEffect : Clutter.ShaderEffect {
58109
if (actor != null) {
59110
actor.notify["width"].connect (update_actor_size);
60111
actor.notify["height"].connect (update_actor_size);
61-
62112
update_actor_size ();
63113
}
64114
}
65115

66-
private void update_actor_size () requires (actor != null) {
116+
private void update_clip_radius () {
117+
float[] _clip_radius = { clip_radius * monitor_scale };
118+
round_pipeline.set_uniform_float (round_clip_radius_location, 1, 1, _clip_radius);
119+
}
120+
121+
private void update_actor_size () {
122+
if (actor == null) {
123+
return;
124+
}
125+
67126
float[] actor_size = {
68-
actor.width * monitor_scale,
69-
actor.height * monitor_scale
127+
actor.width,
128+
actor.height
70129
};
71130

72-
var actor_size_value = GLib.Value (typeof (Clutter.ShaderFloat));
73-
Clutter.Value.set_shader_float (actor_size_value, actor_size);
74-
set_uniform_value ("actor_size", actor_size_value);
131+
round_pipeline.set_uniform_float (round_actor_size_location, 2, 1, actor_size);
132+
}
133+
134+
private void setup_projection_matrix (Cogl.Framebuffer framebuffer, float width, float height) {
135+
Graphene.Matrix projection = {};
136+
projection.init_translate ({ -width / 2.0f, -height / 2.0f, 0.0f });
137+
projection.scale (2.0f / width, -2.0f / height, 1.0f);
138+
139+
framebuffer.set_projection_matrix (projection);
140+
}
141+
142+
private bool update_rounded_fbo (int width, int height) {
143+
#if HAS_MUTTER47
144+
unowned var ctx = actor.context.get_backend ().get_cogl_context ();
145+
#else
146+
unowned var ctx = Clutter.get_default_backend ().get_cogl_context ();
147+
#endif
148+
149+
#if HAS_MUTTER46
150+
round_texture = new Cogl.Texture2D.with_size (ctx, width, height);
151+
#else
152+
var surface = new Cairo.ImageSurface (Cairo.Format.ARGB32, width, height);
153+
try {
154+
round_texture = new Cogl.Texture2D.from_data (ctx, width, height, Cogl.PixelFormat.BGRA_8888_PRE, surface.get_stride (), surface.get_data ());
155+
} catch (Error e) {
156+
critical ("BackgroundBlurEffect: Couldn't create round_texture: %s", e.message);
157+
return false;
158+
}
159+
#endif
160+
161+
round_pipeline.set_layer_texture (0, round_texture);
162+
round_framebuffer = new Cogl.Offscreen.with_texture (round_texture);
163+
164+
setup_projection_matrix (round_framebuffer, width, height);
165+
166+
return true;
167+
}
168+
169+
private bool update_framebuffers (Clutter.PaintContext paint_context) {
170+
if (actor == null) {
171+
return false;
172+
}
173+
174+
var width = (int) actor.width;
175+
var height = (int) actor.height;
176+
177+
if (width <= 0 || height <= 0) {
178+
warning ("RoundedCornersEffect: Couldn't update framebuffers, incorrect size");
179+
return false;
180+
}
181+
182+
return update_rounded_fbo (width, height);
183+
}
184+
185+
private Clutter.PaintNode create_round_nodes (Clutter.PaintNode node) {
186+
var round_node = new Clutter.LayerNode.to_framebuffer (round_framebuffer, round_pipeline);
187+
round_node.add_rectangle ({ 0.0f, 0.0f, actor.width, actor.height });
188+
189+
node.add_child (round_node);
190+
191+
return round_node;
192+
}
193+
194+
private void add_actor_node (Clutter.PaintNode node) {
195+
var actor_node = new Clutter.ActorNode (actor, -1);
196+
node.add_child (actor_node);
197+
}
198+
199+
public override void paint_node (Clutter.PaintNode node, Clutter.PaintContext paint_context, Clutter.EffectPaintFlags flags) {
200+
/* Failing to create or update the offscreen framebuffers prevents
201+
* the entire effect to be applied.
202+
*/
203+
if (!update_framebuffers (paint_context)) {
204+
add_actor_node (node);
205+
return;
206+
}
207+
208+
add_actor_node (create_round_nodes (node));
75209
}
76210
}

0 commit comments

Comments
 (0)