55 */
66
77public 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}
0 commit comments