Skip to content

Commit

Permalink
Fix bounds calculation for subsurface offsets
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
chrisduerr committed Sep 29, 2024
1 parent 1c2dca4 commit 7b31cad
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 2 deletions.
5 changes: 5 additions & 0 deletions src/drawing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<i32, Logical>) {
self.location.replace(self.location.get() + offset);
}

/// Target size after rendering.
pub fn size(&self) -> Size<i32, Logical> {
self.dst_size
Expand Down
48 changes: 46 additions & 2 deletions src/windows/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,11 @@ impl<S: Surface + 'static> Window<S> {
},
|_, _, _| 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`.
Expand Down Expand Up @@ -649,6 +654,7 @@ impl<S: Surface + 'static> Window<S> {
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(
Expand Down Expand Up @@ -689,7 +695,7 @@ impl<S: Surface + 'static> Window<S> {
.data_map
.get::<RefCell<CatacombSurfaceData>>()
.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.
Expand Down Expand Up @@ -983,6 +989,11 @@ pub struct TextureCache {
/// Desired window size during import.
requested_size: Size<i32, Logical>,

/// Offset of the top-left corner for all textures combined.
origin: Option<Point<i32, Logical>>,
/// Distance to the original origin position.
origin_delta: Point<i32, Logical>,

/// Cached textures.
textures: Vec<RenderTexture>,
}
Expand All @@ -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<i32, Logical>) {
// 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<i32, Logical> {
self.geometry_size.unwrap_or(self.texture_size)
Expand Down

0 comments on commit 7b31cad

Please sign in to comment.