Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion data/gala.gresource.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
<gresource prefix="/io/elementary/desktop/gala">
<file compressed="true">shaders/colorblindness-correction.vert</file>
<file compressed="true">shaders/monochrome.vert</file>
<file compressed="true">shaders/rounded-corners.vert</file>
</gresource>
<gresource prefix="/io/elementary/desktop/gala-daemon">
<file compressed="true">gala-daemon.css</file>
Expand Down
59 changes: 0 additions & 59 deletions data/shaders/rounded-corners.vert

This file was deleted.

216 changes: 175 additions & 41 deletions lib/RoundedCornersEffect.vala
Original file line number Diff line number Diff line change
Expand Up @@ -3,48 +3,99 @@
* SPDX-License-Identifier: LGPL-3.0-or-later
*/

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

public float clip_radius {
construct set {
set_uniform_value ("clip_radius", value + CLIP_RADIUS_OFFSET);
}
}

private float _monitor_scale = 1.0f;
public float monitor_scale {
get {
return _monitor_scale;
}
construct set {
_monitor_scale = value;
private Cogl.Framebuffer round_framebuffer;
private Cogl.Pipeline round_pipeline;
private Cogl.Texture round_texture;
private int round_clip_radius_location;
private int round_actor_size_location;

if (actor != null) {
update_actor_size ();
}
}
public RoundedCornersEffect (float clip_radius, float monitor_scale) {
Object (clip_radius: clip_radius, monitor_scale: monitor_scale);
}

public RoundedCornersEffect (float clip_radius, float monitor_scale) {
Object (
#if HAS_MUTTER48
shader_type: Cogl.ShaderType.FRAGMENT,
construct {
#if HAS_MUTTER47
unowned var ctx = actor.context.get_backend ().get_cogl_context ();
#else
shader_type: Clutter.ShaderType.FRAGMENT_SHADER,
unowned var ctx = Clutter.get_default_backend ().get_cogl_context ();
#endif
clip_radius: clip_radius,
monitor_scale: monitor_scale

round_pipeline = new Cogl.Pipeline (ctx);
round_pipeline.set_layer_null_texture (0);
round_pipeline.set_layer_filters (0, Cogl.PipelineFilter.LINEAR, Cogl.PipelineFilter.LINEAR);
round_pipeline.set_layer_wrap_mode (0, Cogl.PipelineWrapMode.CLAMP_TO_EDGE);
round_pipeline.add_snippet (
new Cogl.Snippet (
Cogl.SnippetHook.FRAGMENT,
"""
uniform sampler2D tex;
uniform vec2 actor_size;
uniform float clip_radius;
float rounded_rect_coverage (vec2 p) {
float center_left = clip_radius;
float center_right = actor_size.x - clip_radius;
float center_x;
if (p.x < center_left) {
center_x = center_left;
} else if (p.x > center_right) {
center_x = center_right;
} else {
return 1.0;
}
float center_top = clip_radius;
float center_bottom = actor_size.y - clip_radius;
float center_y;
if (p.y < center_top) {
center_y = center_top;
} else if (p.y > center_bottom) {
center_y = center_bottom;
} else {
return 1.0;
}
vec2 delta = p - vec2 (center_x, center_y);
float dist_squared = dot (delta, delta);
// Fully outside the circle
float outer_radius = clip_radius + 0.5;
if (dist_squared >= (outer_radius * outer_radius)) {
return 0.0;
}
// Fully inside the circle
float inner_radius = clip_radius - 0.5;
if (dist_squared <= (inner_radius * inner_radius)) {
return 1.0;
}
// Only pixels on the edge of the curve need expensive antialiasing
return smoothstep (outer_radius, inner_radius, sqrt (dist_squared));
}
""",

"""
vec4 sample = texture2D (tex, cogl_tex_coord0_in.xy);
vec2 texture_coord = cogl_tex_coord0_in.xy * actor_size;
float res = rounded_rect_coverage (texture_coord);
cogl_color_out = sample * cogl_color_in * res;
"""
)
);
}

construct {
try {
var bytes = GLib.resources_lookup_data ("/io/elementary/desktop/gala/shaders/rounded-corners.vert", GLib.ResourceLookupFlags.NONE);
set_shader_source ((string) bytes.get_data ());
} catch (Error e) {
critical ("Unable to load rounded-corners.vert: %s", e.message);
}
round_clip_radius_location = round_pipeline.get_uniform_location ("clip_radius");
round_actor_size_location = round_pipeline.get_uniform_location ("actor_size");

update_clip_radius ();
update_actor_size ();

notify["monitor-scale"].connect (update_clip_radius);
}

public override void set_actor (Clutter.Actor? new_actor) {
Expand All @@ -58,19 +109,102 @@ public class Gala.RoundedCornersEffect : Clutter.ShaderEffect {
if (actor != null) {
actor.notify["width"].connect (update_actor_size);
actor.notify["height"].connect (update_actor_size);

update_actor_size ();
}
}

private void update_actor_size () requires (actor != null) {
private void update_clip_radius () {
float[] _clip_radius = { clip_radius * monitor_scale };
round_pipeline.set_uniform_float (round_clip_radius_location, 1, 1, _clip_radius);
}

private void update_actor_size () {
if (actor == null) {
return;
}

float[] actor_size = {
actor.width * monitor_scale,
actor.height * monitor_scale
actor.width,
actor.height
};

var actor_size_value = GLib.Value (typeof (Clutter.ShaderFloat));
Clutter.Value.set_shader_float (actor_size_value, actor_size);
set_uniform_value ("actor_size", actor_size_value);
round_pipeline.set_uniform_float (round_actor_size_location, 2, 1, actor_size);
}

private void setup_projection_matrix (Cogl.Framebuffer framebuffer, float width, float height) {
Graphene.Matrix projection = {};
projection.init_translate ({ -width / 2.0f, -height / 2.0f, 0.0f });
projection.scale (2.0f / width, -2.0f / height, 1.0f);

framebuffer.set_projection_matrix (projection);
}

private bool update_rounded_fbo (int width, int height) {
#if HAS_MUTTER47
unowned var ctx = actor.context.get_backend ().get_cogl_context ();
#else
unowned var ctx = Clutter.get_default_backend ().get_cogl_context ();
#endif

#if HAS_MUTTER46
round_texture = new Cogl.Texture2D.with_size (ctx, width, height);
#else
var surface = new Cairo.ImageSurface (Cairo.Format.ARGB32, width, height);
try {
round_texture = new Cogl.Texture2D.from_data (ctx, width, height, Cogl.PixelFormat.BGRA_8888_PRE, surface.get_stride (), surface.get_data ());
} catch (Error e) {
critical ("BackgroundBlurEffect: Couldn't create round_texture: %s", e.message);
return false;
}
#endif

round_pipeline.set_layer_texture (0, round_texture);
round_framebuffer = new Cogl.Offscreen.with_texture (round_texture);

setup_projection_matrix (round_framebuffer, width, height);

return true;
}

private bool update_framebuffers (Clutter.PaintContext paint_context) {
if (actor == null) {
return false;
}

var width = (int) actor.width;
var height = (int) actor.height;

if (width <= 0 || height <= 0) {
warning ("RoundedCornersEffect: Couldn't update framebuffers, incorrect size");
return false;
}

return update_rounded_fbo (width, height);
}

private Clutter.PaintNode create_round_nodes (Clutter.PaintNode node) {
var round_node = new Clutter.LayerNode.to_framebuffer (round_framebuffer, round_pipeline);
round_node.add_rectangle ({ 0.0f, 0.0f, actor.width, actor.height });

node.add_child (round_node);

return round_node;
}

private void add_actor_node (Clutter.PaintNode node) {
var actor_node = new Clutter.ActorNode (actor, -1);
node.add_child (actor_node);
}

public override void paint_node (Clutter.PaintNode node, Clutter.PaintContext paint_context, Clutter.EffectPaintFlags flags) {
/* Failing to create or update the offscreen framebuffers prevents
* the entire effect to be applied.
*/
if (!update_framebuffers (paint_context)) {
add_actor_node (node);
return;
}

add_actor_node (create_round_nodes (node));
}
}
12 changes: 11 additions & 1 deletion src/Widgets/MultitaskingView/WindowClone.vala
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ public class Gala.WindowClone : ActorTarget, RootTarget {

active_shape = new ActiveShape ();
active_shape.opacity = 0;
bind_property ("monitor-scale", active_shape, "monitor-scale", SYNC_CREATE);

clone_container = new Clutter.Actor () {
pivot_point = { 0.5f, 0.5f }
Expand Down Expand Up @@ -624,8 +625,17 @@ public class Gala.WindowClone : ActorTarget, RootTarget {
private const int BORDER_RADIUS = 16;
private const double COLOR_OPACITY = 0.8;

public float monitor_scale {
set {
rounded_corners_effect.monitor_scale = value;
}
}

private RoundedCornersEffect rounded_corners_effect;

construct {
add_effect (new RoundedCornersEffect (BORDER_RADIUS, 1.0f));
rounded_corners_effect = new RoundedCornersEffect (BORDER_RADIUS, 1.0f);
add_effect (rounded_corners_effect);
}

public void update_color () {
Expand Down
4 changes: 4 additions & 0 deletions src/Widgets/WindowSwitcher/WindowSwitcher.vala
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,10 @@ public class Gala.WindowSwitcher : CanvasActor, GestureTarget, RootTarget {

var margin = Utils.scale_to_int (WRAPPER_PADDING, scaling_factor);

foreach (unowned var icon in (GLib.List<weak WindowSwitcherIcon>) container.get_children ()) {
icon.scale_factor = scaling_factor;
}

container.margin_left = margin;
container.margin_right = margin;
container.margin_bottom = margin;
Expand Down