From 35f8d52e0c248bb0056ea25f2b726f195839f0dd Mon Sep 17 00:00:00 2001 From: lenemter Date: Sat, 11 Oct 2025 11:46:59 +0300 Subject: [PATCH 1/5] Fix BackgroundBlurEffect on x2 scaling --- src/BackgroundBlurEffect.vala | 93 ++++++++++++++++++----------------- src/BlurManager.vala | 41 +++++++-------- 2 files changed, 65 insertions(+), 69 deletions(-) diff --git a/src/BackgroundBlurEffect.vala b/src/BackgroundBlurEffect.vala index accf8bdfa..b0a98813e 100644 --- a/src/BackgroundBlurEffect.vala +++ b/src/BackgroundBlurEffect.vala @@ -12,12 +12,14 @@ public class Gala.BackgroundBlurEffect : Clutter.Effect { public float clip_radius { get; construct; } public float monitor_scale { get; construct set; } + public uint left { get; set; default = 0; } + public uint right { get; set; default = 0; } + public uint top { get; set; default = 0; } + public uint bottom { get; set; default = 0; } + private float real_blur_radius; private float downscale_factor; - private int texture_width; - private int texture_height; - private Cogl.Framebuffer actor_framebuffer; private Cogl.Pipeline actor_pipeline; private Cogl.Texture actor_texture; @@ -223,16 +225,13 @@ public class Gala.BackgroundBlurEffect : Clutter.Effect { framebuffer.set_projection_matrix (projection); } - private bool update_actor_fbo (int width, int height, float downscale_factor) { - if (width <= 0 || height <= 0) { - return false; - } - + private bool update_actor_fbo (int width, int height, float new_downscale_factor) { if ( - texture_width == width && - texture_height == height && - this.downscale_factor == downscale_factor && - actor_framebuffer != null + actor_framebuffer != null && + actor_texture != null && + actor_texture.get_width () == width && + actor_texture.get_height () == height && + downscale_factor == new_downscale_factor ) { return true; } @@ -243,8 +242,8 @@ public class Gala.BackgroundBlurEffect : Clutter.Effect { unowned var ctx = Clutter.get_default_backend ().get_cogl_context (); #endif - var new_width = (int) Math.floorf (width / downscale_factor); - var new_height = (int) Math.floorf (height / downscale_factor); + var new_width = (int) Math.floorf (width / new_downscale_factor); + var new_height = (int) Math.floorf (height / new_downscale_factor); #if HAS_MUTTER46 actor_texture = new Cogl.Texture2D.with_size (ctx, new_width, new_height); @@ -266,12 +265,13 @@ public class Gala.BackgroundBlurEffect : Clutter.Effect { return true; } - private bool update_rounded_fbo (int width, int height, float downscale_factor) { + private bool update_rounded_fbo (int width, int height, float new_downscale_factor) { if ( - texture_width == width && - texture_height == height && - this.downscale_factor == downscale_factor && - round_framebuffer != null + round_framebuffer != null && + round_texture != null && + round_texture.get_width () == width && + round_texture.get_height () == height && + downscale_factor == new_downscale_factor ) { return true; } @@ -282,8 +282,8 @@ public class Gala.BackgroundBlurEffect : Clutter.Effect { unowned var ctx = Clutter.get_default_backend ().get_cogl_context (); #endif - var new_width = (int) Math.floorf (width / downscale_factor); - var new_height = (int) Math.floorf (height / downscale_factor); + var new_width = (int) Math.floorf (width / new_downscale_factor); + var new_height = (int) Math.floorf (height / new_downscale_factor); #if HAS_MUTTER46 round_texture = new Cogl.Texture2D.with_size (ctx, new_width, new_height); @@ -307,9 +307,10 @@ public class Gala.BackgroundBlurEffect : Clutter.Effect { private bool update_background_fbo (int width, int height) { if ( - texture_width == width && - texture_height == height && - background_framebuffer != null + background_framebuffer != null && + background_texture != null && + background_texture.get_width () == width && + background_texture.get_height () == height ) { return true; } @@ -341,32 +342,34 @@ public class Gala.BackgroundBlurEffect : Clutter.Effect { } private bool update_framebuffers (Clutter.PaintContext paint_context, Clutter.ActorBox actor_box) { - var width = (int) actor_box.get_width (); - var height = (int) actor_box.get_height (); + var actor_width = (int) actor_box.get_width (); + var actor_height = (int) actor_box.get_height (); - if (width < 0 || height < 0) { + var blur_width = (int) (actor_width - left - right); + var blur_height = (int) (actor_height - top - bottom); + + if (actor_width <= 0 || actor_height <= 0 || blur_width <= 0 || blur_height <= 0) { warning ("BackgroundBlurEffect: Couldn't update framebuffers, incorrect size"); return false; } - var downscale_factor = calculate_downscale_factor (width, height, real_blur_radius); + var new_downscale_factor = calculate_downscale_factor (blur_width, blur_height, real_blur_radius); - var updated = update_actor_fbo (width, height, downscale_factor) && update_rounded_fbo (width, height, downscale_factor) && update_background_fbo (width, height); + var updated = ( + update_actor_fbo (actor_width, actor_height, new_downscale_factor) && + update_rounded_fbo (blur_width, blur_height, new_downscale_factor) && + update_background_fbo (blur_width, blur_height) + ); - texture_width = width; - texture_height = height; - this.downscale_factor = downscale_factor; + downscale_factor = new_downscale_factor; return updated; } private Clutter.PaintNode create_blur_nodes (Clutter.PaintNode node) { - float width, height; - actor.get_size (out width, out height); - var blur_node = new Clutter.BlurNode ( - (uint) (texture_width / downscale_factor), - (uint) (texture_height / downscale_factor), + (uint) (round_texture.get_width () / downscale_factor), + (uint) (round_texture.get_height () / downscale_factor), real_blur_radius / downscale_factor ); blur_node.add_rectangle ({ @@ -378,7 +381,7 @@ public class Gala.BackgroundBlurEffect : Clutter.Effect { var round_node = new Clutter.LayerNode.to_framebuffer (round_framebuffer, round_pipeline); round_node.add_child (blur_node); - round_node.add_rectangle ({ 0.0f, 0.0f, width, height }); + round_node.add_rectangle ({ left, top, actor_texture.get_width () - right, actor_texture.get_height () - bottom }); node.add_child (round_node); @@ -391,25 +394,23 @@ public class Gala.BackgroundBlurEffect : Clutter.Effect { } private void paint_background (Clutter.PaintNode node, Clutter.PaintContext paint_context, Clutter.ActorBox source_actor_box) { - float transformed_x, transformed_y, transformed_width, transformed_height; - + float transformed_x, transformed_y; source_actor_box.get_origin (out transformed_x, out transformed_y); - source_actor_box.get_size (out transformed_width, out transformed_height); /* Background layer node */ var background_node = new Clutter.LayerNode.to_framebuffer (background_framebuffer, background_pipeline); node.add_child (background_node); - background_node.add_rectangle ({ 0.0f, 0.0f, texture_width / downscale_factor, texture_height / downscale_factor }); + background_node.add_rectangle ({ 0.0f, 0.0f, background_texture.get_width () / downscale_factor, background_texture.get_height () / downscale_factor }); /* Blit node */ var blit_node = new Clutter.BlitNode (paint_context.get_framebuffer ()); background_node.add_child (blit_node); blit_node.add_blit_rectangle ( - (int) transformed_x, - (int) transformed_y, + (int) (transformed_x + left), + (int) (transformed_y + top), 0, 0, - (int) transformed_width, - (int) transformed_height + (int) background_texture.get_width (), + (int) background_texture.get_height () ); } diff --git a/src/BlurManager.vala b/src/BlurManager.vala index 447bc397b..d0662d60d 100644 --- a/src/BlurManager.vala +++ b/src/BlurManager.vala @@ -5,7 +5,6 @@ public class Gala.BlurManager : Object { private struct BlurData { - Clutter.Actor actor; BackgroundBlurEffect blur_effect; uint left; uint right; @@ -55,44 +54,40 @@ public class Gala.BlurManager : Object { return; } + var buffer_rect = window.get_buffer_rect (); + var frame_rect = window.get_frame_rect (); + var left_shadow_size = frame_rect.x - buffer_rect.x; + var right_shadow_size = buffer_rect.width - frame_rect.width - left_shadow_size; + var top_shadow_size = frame_rect.y - buffer_rect.y; + var bottom_shadow_size = buffer_rect.height - frame_rect.height - top_shadow_size; + var blur_data = blurred_windows[window]; if (blur_data == null) { var blur_effect = new BackgroundBlurEffect (BLUR_RADIUS, (int) clip_radius, 1.0f); - var blurred_actor = new Clutter.Actor (); - blurred_actor.add_effect (blur_effect); - window_actor.insert_child_below (blurred_actor, null); + window_actor.add_effect (blur_effect); - blur_data = { blurred_actor, blur_effect, left, right, top, bottom, clip_radius }; + blur_data = { blur_effect, left, right, top, bottom, clip_radius }; blurred_windows[window] = blur_data; window.size_changed.connect (on_size_changed); } - var buffer_rect = window.get_buffer_rect (); - var frame_rect = window.get_frame_rect (); - var x_shadow_size = frame_rect.x - buffer_rect.x; - var y_shadow_size = frame_rect.y - buffer_rect.y; - - blur_data.actor.set_position (x_shadow_size + left, y_shadow_size + top); - blur_data.actor.set_size (frame_rect.width - left - right, frame_rect.height - top - bottom); + blur_data.blur_effect.left = left_shadow_size + left; + blur_data.blur_effect.right = right_shadow_size + right; + blur_data.blur_effect.top = top_shadow_size + top; + blur_data.blur_effect.bottom = bottom_shadow_size + bottom; } public void remove_blur (Meta.Window window) { - var blur_data = blurred_windows[window]; - if (blur_data == null) { - return; - } - - var actor = blur_data.actor; - actor.remove_effect (blur_data.blur_effect); + unowned var window_actor = (Meta.WindowActor) window.get_compositor_private (); + var blur_data = blurred_windows.take (window); - unowned var parent = actor.get_parent (); - if (parent != null) { - parent.remove_child (actor); + if (blur_data == null || window_actor == null) { + return; } - blurred_windows.remove (window); + window_actor.remove_effect (blur_data.blur_effect); } private void on_size_changed (Meta.Window window) { From 9d4e19335ed5d6afe6f1c1e9028f3612b18bd0c7 Mon Sep 17 00:00:00 2001 From: lenemter Date: Sat, 11 Oct 2025 11:59:44 +0300 Subject: [PATCH 2/5] Fix rounded corners --- src/BackgroundBlurEffect.vala | 49 +++++++++++------------------------ 1 file changed, 15 insertions(+), 34 deletions(-) diff --git a/src/BackgroundBlurEffect.vala b/src/BackgroundBlurEffect.vala index b0a98813e..3f50883ef 100644 --- a/src/BackgroundBlurEffect.vala +++ b/src/BackgroundBlurEffect.vala @@ -124,40 +124,6 @@ public class Gala.BackgroundBlurEffect : Clutter.Effect { 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) { - if (actor != null) { - actor.notify["width"].disconnect (update_actor_size); - actor.notify["height"].disconnect (update_actor_size); - } - - base.set_actor (new_actor); - - if (actor != null) { - actor.notify["width"].connect (update_actor_size); - actor.notify["height"].connect (update_actor_size); - update_actor_size (); - } - } - - 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 () { - float[] actor_size = { - actor.width, - actor.height - }; - - round_pipeline.set_uniform_float (round_actor_size_location, 2, 1, actor_size); } private void update_actor_box (Clutter.PaintContext paint_context, ref Clutter.ActorBox source_actor_box) { @@ -444,6 +410,21 @@ public class Gala.BackgroundBlurEffect : Clutter.Effect { var blur_node = create_blur_nodes (node); paint_background (blur_node, paint_context, source_actor_box); add_actor_node (node); + + update_uniforms (); + } + + private void update_uniforms () requires (round_texture != null || actor_texture != null) { + float[] actor_size = { + round_texture.get_width (), + round_texture.get_height () + }; + round_pipeline.set_uniform_float (round_actor_size_location, 2, 1, actor_size); + + float[] clip_vals = { + (clip_radius * monitor_scale) / downscale_factor + }; + round_pipeline.set_uniform_float (round_clip_radius_location, 1, 1, clip_vals); } public override void paint (Clutter.PaintNode node, Clutter.PaintContext paint_context, Clutter.EffectPaintFlags flags) { From 80c213106f3aaebccad81cdbceeca4927a4ab527 Mon Sep 17 00:00:00 2001 From: lenemter Date: Sat, 11 Oct 2025 12:09:24 +0300 Subject: [PATCH 3/5] Cleanup and scale corners --- src/BackgroundBlurEffect.vala | 9 +++++---- src/BlurManager.vala | 28 ++++++++++++++++++++-------- 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/src/BackgroundBlurEffect.vala b/src/BackgroundBlurEffect.vala index 3f50883ef..70d948e13 100644 --- a/src/BackgroundBlurEffect.vala +++ b/src/BackgroundBlurEffect.vala @@ -334,8 +334,8 @@ public class Gala.BackgroundBlurEffect : Clutter.Effect { private Clutter.PaintNode create_blur_nodes (Clutter.PaintNode node) { var blur_node = new Clutter.BlurNode ( - (uint) (round_texture.get_width () / downscale_factor), - (uint) (round_texture.get_height () / downscale_factor), + round_texture.get_width (), + round_texture.get_height (), real_blur_radius / downscale_factor ); blur_node.add_rectangle ({ @@ -366,7 +366,7 @@ public class Gala.BackgroundBlurEffect : Clutter.Effect { /* Background layer node */ var background_node = new Clutter.LayerNode.to_framebuffer (background_framebuffer, background_pipeline); node.add_child (background_node); - background_node.add_rectangle ({ 0.0f, 0.0f, background_texture.get_width () / downscale_factor, background_texture.get_height () / downscale_factor }); + background_node.add_rectangle ({ 0.0f, 0.0f, background_texture.get_width (), background_texture.get_height () }); /* Blit node */ var blit_node = new Clutter.BlitNode (paint_context.get_framebuffer ()); @@ -374,7 +374,8 @@ public class Gala.BackgroundBlurEffect : Clutter.Effect { blit_node.add_blit_rectangle ( (int) (transformed_x + left), (int) (transformed_y + top), - 0, 0, + 0, + 0, (int) background_texture.get_width (), (int) background_texture.get_height () ); diff --git a/src/BlurManager.vala b/src/BlurManager.vala index d0662d60d..6fcd813f9 100644 --- a/src/BlurManager.vala +++ b/src/BlurManager.vala @@ -42,6 +42,13 @@ public class Gala.BlurManager : Object { window.notify["mutter-hints"].connect ((obj, pspec) => parse_mutter_hints ((Meta.Window) obj)); parse_mutter_hints (window); }); + + unowned var monitor_manager = wm.get_display ().get_context ().get_backend ().get_monitor_manager (); + monitor_manager.monitors_changed.connect (() => { + foreach (unowned var window in blurred_windows.get_keys ()) { + blurred_windows[window].blur_effect.monitor_scale = window.display.get_monitor_scale (window.get_monitor ()); + } + }); } /** @@ -54,25 +61,30 @@ public class Gala.BlurManager : Object { return; } - var buffer_rect = window.get_buffer_rect (); - var frame_rect = window.get_frame_rect (); - var left_shadow_size = frame_rect.x - buffer_rect.x; - var right_shadow_size = buffer_rect.width - frame_rect.width - left_shadow_size; - var top_shadow_size = frame_rect.y - buffer_rect.y; - var bottom_shadow_size = buffer_rect.height - frame_rect.height - top_shadow_size; - var blur_data = blurred_windows[window]; if (blur_data == null) { - var blur_effect = new BackgroundBlurEffect (BLUR_RADIUS, (int) clip_radius, 1.0f); + var blur_effect = new BackgroundBlurEffect ( + BLUR_RADIUS, + (int) clip_radius, + window.display.get_monitor_scale (window.get_monitor ()) + ); window_actor.add_effect (blur_effect); blur_data = { blur_effect, left, right, top, bottom, clip_radius }; blurred_windows[window] = blur_data; + // TODO: We can require users of blur API to calculate shadow_size themselves and remove connecting to this window.size_changed.connect (on_size_changed); } + var buffer_rect = window.get_buffer_rect (); + var frame_rect = window.get_frame_rect (); + var left_shadow_size = frame_rect.x - buffer_rect.x; + var right_shadow_size = buffer_rect.width - frame_rect.width - left_shadow_size; + var top_shadow_size = frame_rect.y - buffer_rect.y; + var bottom_shadow_size = buffer_rect.height - frame_rect.height - top_shadow_size; + blur_data.blur_effect.left = left_shadow_size + left; blur_data.blur_effect.right = right_shadow_size + right; blur_data.blur_effect.top = top_shadow_size + top; From 721dc0272900a247eae04746625beb4143a473d6 Mon Sep 17 00:00:00 2001 From: lenemter Date: Sat, 11 Oct 2025 12:12:18 +0300 Subject: [PATCH 4/5] Oops --- src/BackgroundBlurEffect.vala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/BackgroundBlurEffect.vala b/src/BackgroundBlurEffect.vala index 70d948e13..e69f8a815 100644 --- a/src/BackgroundBlurEffect.vala +++ b/src/BackgroundBlurEffect.vala @@ -415,7 +415,7 @@ public class Gala.BackgroundBlurEffect : Clutter.Effect { update_uniforms (); } - private void update_uniforms () requires (round_texture != null || actor_texture != null) { + private void update_uniforms () requires (round_texture != null) { float[] actor_size = { round_texture.get_width (), round_texture.get_height () From f179d5934dcee72168e8b476a52317330bc6efa3 Mon Sep 17 00:00:00 2001 From: lenemter Date: Sun, 12 Oct 2025 16:15:20 +0300 Subject: [PATCH 5/5] BackgroundBlurEffect: get scale factor from stage view --- src/BackgroundBlurEffect.vala | 32 +++++++++---------- src/BlurManager.vala | 13 +------- .../WindowSwitcher/WindowSwitcher.vala | 3 +- 3 files changed, 17 insertions(+), 31 deletions(-) diff --git a/src/BackgroundBlurEffect.vala b/src/BackgroundBlurEffect.vala index e69f8a815..dad30c2ac 100644 --- a/src/BackgroundBlurEffect.vala +++ b/src/BackgroundBlurEffect.vala @@ -10,7 +10,6 @@ public class Gala.BackgroundBlurEffect : Clutter.Effect { public float blur_radius { get; construct; } public float clip_radius { get; construct; } - public float monitor_scale { get; construct set; } public uint left { get; set; default = 0; } public uint right { get; set; default = 0; } @@ -36,8 +35,8 @@ public class Gala.BackgroundBlurEffect : Clutter.Effect { private int frame_counter = 0; - public BackgroundBlurEffect (float blur_radius, float clip_radius, float monitor_scale) { - Object (blur_radius: blur_radius, clip_radius: clip_radius, monitor_scale: monitor_scale); + public BackgroundBlurEffect (float blur_radius, float clip_radius) { + Object (blur_radius: blur_radius, clip_radius: clip_radius); } construct { @@ -126,28 +125,27 @@ public class Gala.BackgroundBlurEffect : Clutter.Effect { round_actor_size_location = round_pipeline.get_uniform_location ("actor_size"); } - private void update_actor_box (Clutter.PaintContext paint_context, ref Clutter.ActorBox source_actor_box) { - float box_scale_factor = 1.0f; - float origin_x, origin_y; - float width, height; + private float get_stage_view_scale (Clutter.PaintContext paint_context) { + unowned var stage_view = paint_context.get_stage_view (); - var stage_view = paint_context.get_stage_view (); + return stage_view != null ? stage_view.scale : 1.0f; + } + private void update_actor_box (Clutter.PaintContext paint_context, ref Clutter.ActorBox source_actor_box) { + float origin_x, origin_y, width, height; actor.get_transformed_position (out origin_x, out origin_y); actor.get_transformed_size (out width, out height); + + float box_scale_factor = get_stage_view_scale (paint_context); + var stage_view = paint_context.get_stage_view (); if (stage_view != null) { Mtk.Rectangle stage_view_layout = {}; - box_scale_factor = stage_view.get_scale (); stage_view.get_layout (ref stage_view_layout); origin_x -= stage_view_layout.x; origin_y -= stage_view_layout.y; - } else { - /* If we're drawing off stage, just assume scale = 1, this won't work - * with stage-view scaling though. - */ } source_actor_box.set_origin (origin_x, origin_y); @@ -395,7 +393,7 @@ public class Gala.BackgroundBlurEffect : Clutter.Effect { } var relative_opacity = (float) actor.opacity / 255.0f; - real_blur_radius = blur_radius * (float) Math.pow (relative_opacity, 2) * monitor_scale * (float) total_scale; + real_blur_radius = blur_radius * (float) Math.pow (relative_opacity, 2) * get_stage_view_scale (paint_context) * (float) total_scale; Clutter.ActorBox source_actor_box = {}; update_actor_box (paint_context, ref source_actor_box); @@ -412,10 +410,10 @@ public class Gala.BackgroundBlurEffect : Clutter.Effect { paint_background (blur_node, paint_context, source_actor_box); add_actor_node (node); - update_uniforms (); + update_uniforms (paint_context); } - private void update_uniforms () requires (round_texture != null) { + private void update_uniforms (Clutter.PaintContext paint_context) requires (round_texture != null) { float[] actor_size = { round_texture.get_width (), round_texture.get_height () @@ -423,7 +421,7 @@ public class Gala.BackgroundBlurEffect : Clutter.Effect { round_pipeline.set_uniform_float (round_actor_size_location, 2, 1, actor_size); float[] clip_vals = { - (clip_radius * monitor_scale) / downscale_factor + (clip_radius * get_stage_view_scale (paint_context)) / downscale_factor }; round_pipeline.set_uniform_float (round_clip_radius_location, 1, 1, clip_vals); } diff --git a/src/BlurManager.vala b/src/BlurManager.vala index 6fcd813f9..4ca8a9b90 100644 --- a/src/BlurManager.vala +++ b/src/BlurManager.vala @@ -42,13 +42,6 @@ public class Gala.BlurManager : Object { window.notify["mutter-hints"].connect ((obj, pspec) => parse_mutter_hints ((Meta.Window) obj)); parse_mutter_hints (window); }); - - unowned var monitor_manager = wm.get_display ().get_context ().get_backend ().get_monitor_manager (); - monitor_manager.monitors_changed.connect (() => { - foreach (unowned var window in blurred_windows.get_keys ()) { - blurred_windows[window].blur_effect.monitor_scale = window.display.get_monitor_scale (window.get_monitor ()); - } - }); } /** @@ -63,11 +56,7 @@ public class Gala.BlurManager : Object { var blur_data = blurred_windows[window]; if (blur_data == null) { - var blur_effect = new BackgroundBlurEffect ( - BLUR_RADIUS, - (int) clip_radius, - window.display.get_monitor_scale (window.get_monitor ()) - ); + var blur_effect = new BackgroundBlurEffect (BLUR_RADIUS, (int) clip_radius); window_actor.add_effect (blur_effect); diff --git a/src/Widgets/WindowSwitcher/WindowSwitcher.vala b/src/Widgets/WindowSwitcher/WindowSwitcher.vala index 9b3d12fc5..176af9a19 100644 --- a/src/Widgets/WindowSwitcher/WindowSwitcher.vala +++ b/src/Widgets/WindowSwitcher/WindowSwitcher.vala @@ -102,7 +102,7 @@ public class Gala.WindowSwitcher : CanvasActor, GestureTarget, RootTarget { add_effect (shadow_effect); - blur_effect = new BackgroundBlurEffect (40, 9, scaling_factor); + blur_effect = new BackgroundBlurEffect (40, 9); add_effect (blur_effect); scale (); @@ -123,7 +123,6 @@ public class Gala.WindowSwitcher : CanvasActor, GestureTarget, RootTarget { scaling_factor = display.get_monitor_scale (display.get_current_monitor ()); shadow_effect.monitor_scale = scaling_factor; - blur_effect.monitor_scale = scaling_factor; var margin = Utils.scale_to_int (WRAPPER_PADDING, scaling_factor);