Skip to content

Commit f6e38fd

Browse files
authored
Rewrite ShadowEffect (#2433)
1 parent c36970d commit f6e38fd

File tree

3 files changed

+100
-42
lines changed

3 files changed

+100
-42
lines changed

lib/ShadowEffect.vala

Lines changed: 98 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
*/
66

77
public class Gala.ShadowEffect : Clutter.Effect {
8+
private const float INITIAL_OPACITY = 0.25f;
9+
810
private class Shadow {
911
public int users;
1012
public Cogl.Texture texture;
@@ -38,13 +40,13 @@ public class Gala.ShadowEffect : Clutter.Effect {
3840
_css_class = value;
3941
switch (value) {
4042
case "workspace-switcher":
41-
shadow_size = 6;
43+
shadow_size = 3;
4244
break;
4345
case "window":
44-
shadow_size = 55;
46+
shadow_size = 26;
4547
break;
4648
default:
47-
shadow_size = 18;
49+
shadow_size = 9;
4850
break;
4951
}
5052
}
@@ -100,43 +102,12 @@ public class Gala.ShadowEffect : Clutter.Effect {
100102
return shadow.texture;
101103
}
102104

103-
// fill a new texture for this size
104-
var buffer = new Drawing.BufferSurface (width, height);
105-
Drawing.Utilities.cairo_rounded_rectangle (
106-
buffer.context,
107-
shadow_size,
108-
shadow_size,
109-
width - shadow_size * 2,
110-
height - shadow_size * 2,
111-
border_radius
112-
);
113-
114-
buffer.context.set_source_rgba (0, 0, 0, 0.7);
115-
buffer.context.fill ();
116-
117-
buffer.exponential_blur (shadow_size / 2);
118-
119-
var surface = new Cairo.ImageSurface (Cairo.Format.ARGB32, width, height);
120-
var cr = new Cairo.Context (surface);
121-
cr.set_source_surface (buffer.surface, 0, 0);
122-
cr.paint ();
123-
124-
cr.save ();
125-
cr.set_operator (Cairo.Operator.CLEAR);
126-
Drawing.Utilities.cairo_rounded_rectangle (cr, shadow_size, shadow_size, actor.width, actor.height, border_radius);
127-
cr.fill ();
128-
cr.restore ();
129-
130-
try {
131-
var texture = new Cogl.Texture2D.from_data (context, width, height, Cogl.PixelFormat.BGRA_8888_PRE,
132-
surface.get_stride (), surface.get_data ());
105+
var texture = get_shadow_texture (context, width, height, shadow_size, border_radius);
106+
if (texture != null) {
133107
shadow_cache.@set (current_key, new Shadow (texture));
134-
135-
return texture;
136-
} catch (Error e) {
137-
debug (e.message);
138-
return null;
139108
}
109+
110+
return texture;
140111
}
141112

142113
public override void paint (Clutter.PaintNode node, Clutter.PaintContext context, Clutter.EffectPaintFlags flags) {
@@ -149,8 +120,8 @@ public class Gala.ShadowEffect : Clutter.Effect {
149120
pipeline.set_layer_texture (0, shadow);
150121
}
151122

152-
var opacity = actor.get_paint_opacity () * shadow_opacity / 255.0f;
153-
var alpha = Cogl.Color.from_4f (1.0f, 1.0f, 1.0f, opacity / 255.0f);
123+
var opacity = actor.get_paint_opacity () * shadow_opacity * INITIAL_OPACITY / 255.0f / 255.0f;
124+
var alpha = Cogl.Color.from_4f (1.0f, 1.0f, 1.0f, opacity);
154125
alpha.premultiply ();
155126

156127
pipeline.set_color (alpha);
@@ -220,4 +191,91 @@ public class Gala.ShadowEffect : Clutter.Effect {
220191
return Source.REMOVE;
221192
});
222193
}
194+
195+
private Cogl.Texture? get_shadow_texture (Cogl.Context context, int width, int height, int shadow_size, int corner_radius) {
196+
var data = new uint8[width * height];
197+
198+
// use fast Gaussian blur approximation
199+
var precomputed_colors = new uint8[shadow_size + 1];
200+
for (var i = 0; i <= shadow_size; i++) {
201+
var normalized = (double) i / shadow_size;
202+
precomputed_colors[i] = (uint8) (normalized * normalized * (3.0 - 2.0 * normalized) * 255.0);
203+
}
204+
205+
var total_offset = shadow_size + corner_radius;
206+
207+
var target_row = height - total_offset;
208+
for (var row = total_offset; row < target_row; row++) {
209+
var current_row = row * width;
210+
var current_row_end = current_row + width - 1;
211+
for (int i = 0; i <= shadow_size; i++) {
212+
var current_color = precomputed_colors[i];
213+
214+
data[current_row + i] = current_color;
215+
data[current_row_end - i] = current_color;
216+
}
217+
}
218+
219+
var target_col = width - total_offset;
220+
for (var row = 0; row <= shadow_size; row++) {
221+
var current_row = row * width;
222+
var end_row = (height - row) * width - 1;
223+
var current_color = precomputed_colors[row];
224+
225+
for (var col = total_offset; col < target_col; col++) {
226+
data[current_row + col] = current_color;
227+
data[end_row - col] = current_color;
228+
}
229+
}
230+
231+
var target_square = shadow_size + corner_radius;
232+
for (var y = 0; y < target_square; y++) {
233+
var current_row = width * y;
234+
var current_row_end = current_row + width - 1;
235+
var end_row = (height - 1 - y) * width;
236+
var end_row_end = end_row + width - 1;
237+
238+
var dy = target_square - y;
239+
var dy_squared = dy * dy;
240+
241+
for (var x = 0; x < target_square; x++) {
242+
var dx = target_square - x;
243+
var squared_distance = dx * dx + dy_squared;
244+
245+
if (squared_distance > target_square * target_square) {
246+
continue;
247+
}
248+
249+
if (squared_distance >= corner_radius * corner_radius) {
250+
double sin, cos;
251+
Math.sincos (Math.atan2 (dy, dx), out sin, out cos);
252+
253+
var real_dx = dx - corner_radius * cos;
254+
var real_dy = dy - corner_radius * sin;
255+
256+
// use fast Gaussian blur approximation
257+
var normalized = (double) Math.sqrt (real_dx * real_dx + real_dy * real_dy) / shadow_size;
258+
var current_color = (uint8) (1.0 - normalized * normalized * (3.0 - 2.0 * normalized) * 255.0);
259+
260+
// when we're very close to the rounded corner, our real_distance can be wrong (idk why).
261+
// If we're here, we're not inside the corner yet and that means we must draw something
262+
if (current_color == 0) {
263+
current_color = 255;
264+
}
265+
266+
data[current_row + x] = current_color;
267+
data[current_row_end - x] = current_color;
268+
data[end_row + x] = current_color;
269+
data[end_row_end - x] = current_color;
270+
}
271+
}
272+
}
273+
274+
try {
275+
return new Cogl.Texture2D.from_data (context, width, height, Cogl.PixelFormat.A_8, width, data);
276+
} catch (Error e) {
277+
warning ("ShadowEffect: Couldn't create texture");
278+
return null;
279+
}
280+
}
223281
}

src/Widgets/MultitaskingView/IconGroup.vala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ public class Gala.IconGroup : CanvasActor {
236236
);
237237

238238
var shadow_effect = new ShadowEffect ("", scale_factor) {
239-
border_radius = Utils.scale_to_int (5, scale_factor)
239+
border_radius = 6
240240
};
241241

242242
var style_manager = Drawing.StyleManager.get_instance ();

src/Widgets/WindowSwitcher/WindowSwitcher.vala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ public class Gala.WindowSwitcher : CanvasActor, GestureTarget, RootTarget {
9898
};
9999

100100
shadow_effect = new ShadowEffect ("window-switcher", scaling_factor) {
101-
border_radius = Utils.scale_to_int (9, scaling_factor),
101+
border_radius = 10,
102102
shadow_opacity = 100
103103
};
104104
add_effect (shadow_effect);

0 commit comments

Comments
 (0)