From 7b31cad95046a38acee5c99070cdf64456c56860 Mon Sep 17 00:00:00 2001 From: Christian Duerr Date: Sun, 29 Sep 2024 05:34:45 +0200 Subject: [PATCH] Fix bounds calculation for subsurface offsets This fixes an issue with the calculated bounds for windows where the top-left corner of the window would not be at `(0, 0)`. To fix this issue while keeping the math simple, we check the texture cache after it has been populated and move all textures to place the top-left corner at `(0, 0)`. This has the advantage of keeping the render origin at `(0, 0)`, so we don't have to adjust the window's cropping bounds to deal with negative locations. The new logic matches what we're already doing if a geometry is set, with the only difference being that we know the geometry's offset ahead of time while having to import all surfaces to find it when the geometry is not set. Closes #158. --- src/drawing.rs | 5 +++++ src/windows/window.rs | 48 +++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/src/drawing.rs b/src/drawing.rs index 7fcf044..e93cf44 100644 --- a/src/drawing.rs +++ b/src/drawing.rs @@ -154,6 +154,11 @@ impl Texture { self.location.replace(location); } + /// Move this texture's location by an offset. + pub fn offset_location(&self, offset: Point) { + self.location.replace(self.location.get() + offset); + } + /// Target size after rendering. pub fn size(&self) -> Size { self.dst_size diff --git a/src/windows/window.rs b/src/windows/window.rs index 0a8ad8c..8739683 100644 --- a/src/windows/window.rs +++ b/src/windows/window.rs @@ -346,6 +346,11 @@ impl Window { }, |_, _, _| true, ); + + // Ensure combined top-left corner of the texture cache is normalized to (0, 0). + if geometry.is_none() { + self.texture_cache.reset_origin(); + } } /// Mark all rendered clients as presented for `wp_presentation`. @@ -649,6 +654,7 @@ impl Window { position = position.upscale(output_scale / window_scale); let geometry = self.surface.geometry().unwrap_or_default(); + let origin_delta = self.texture_cache.origin_delta; let result = RefCell::new(None); compositor::with_surface_tree_downward( @@ -689,7 +695,7 @@ impl Window { .data_map .get::>() .map_or_else(Size::default, |data| data.borrow().dst_size); - let surface_loc = location - geometry.loc.to_f64(); + let surface_loc = location - geometry.loc.to_f64() - origin_delta.to_f64(); let surface_rect = Rectangle::from_loc_and_size(surface_loc, size.to_f64()); // Get input region and its relative touch position. @@ -983,6 +989,11 @@ pub struct TextureCache { /// Desired window size during import. requested_size: Size, + /// Offset of the top-left corner for all textures combined. + origin: Option>, + /// Distance to the original origin position. + origin_delta: Point, + /// Cached textures. textures: Vec, } @@ -996,18 +1007,51 @@ impl TextureCache { ) { self.requested_size = requested_size; self.geometry_size = geometry_size; + self.texture_size = Default::default(); + self.origin_delta = Default::default(); + self.origin = Default::default(); self.textures.clear(); } /// Add a new texture. fn push(&mut self, texture: RenderTexture, location: Point) { + // Update the combined texture origin. + let origin = self.origin.get_or_insert(location); + origin.x = origin.x.min(location.x); + origin.y = origin.y.min(location.y); + // Update the combined texture size. - let max_size = texture.size() + location.max((0, 0)).to_size(); + let max_size = texture.size() + location.to_size(); self.texture_size = self.texture_size.max(max_size); self.textures.push(texture); } + /// Reset the combined texture origin to zero. + /// + /// Consistently having an origin of zero for the combined top-left of all + /// textures ensures the rendering location and cropping bounds are + /// identical, which makes reasoning about them easier in the rest of + /// the code. + fn reset_origin(&mut self) { + let origin = match self.origin.take() { + Some(origin) if origin != Point::default() => origin, + _ => return, + }; + + // Move each texture relative to the new origin. + for texture in &mut self.textures { + texture.offset_location(Point::default() - origin); + } + + // Update size to account for offset. + self.texture_size.w -= origin.x; + self.texture_size.h -= origin.y; + + // Track the old origin, for input handling. + self.origin_delta += origin; + } + /// Get the size of the cached texture. fn size(&self) -> Size { self.geometry_size.unwrap_or(self.texture_size)