Consistently use buffer coordinates

- Moves `Transform` into utils::geometry
- Changes conversion from and into buffer-coordinates to take
  `Transform` arguments.
- `Renderer` take `Buffer`-space damage now
- buffer_transform is taken into account everywhere
This commit is contained in:
Victor Brekenfeld 2022-01-16 19:28:28 +01:00 committed by Victoria Brekenfeld
parent f76311227b
commit 439d5a7820
8 changed files with 316 additions and 257 deletions

View File

@ -839,7 +839,7 @@ impl EGLBufferReader {
pub fn egl_buffer_dimensions(
&self,
buffer: &WlBuffer,
) -> Option<crate::utils::Size<i32, crate::utils::Physical>> {
) -> Option<crate::utils::Size<i32, crate::utils::Buffer>> {
if !buffer.as_ref().is_alive() {
debug!(self.logger, "Suplied buffer is no longer alive");
return None;

View File

@ -13,7 +13,7 @@ use cgmath::{prelude::*, Matrix3, Vector2, Vector3};
mod shaders;
mod version;
use super::{Bind, Frame, Renderer, Texture, TextureFilter, Transform, Unbind};
use super::{Bind, Frame, Renderer, Texture, TextureFilter, Unbind};
use crate::backend::allocator::{
dmabuf::{Dmabuf, WeakDmabuf},
Format,
@ -23,7 +23,7 @@ use crate::backend::egl::{
EGLContext, EGLSurface, MakeCurrentError,
};
use crate::backend::SwapBuffersError;
use crate::utils::{Buffer, Physical, Rectangle, Size};
use crate::utils::{Buffer, Physical, Rectangle, Size, Transform};
#[cfg(all(feature = "wayland_frontend", feature = "use_system_lib"))]
use super::ImportEgl;
@ -1250,7 +1250,7 @@ impl Frame for Gles2Frame {
texture: &Self::TextureId,
src: Rectangle<i32, Buffer>,
dest: Rectangle<f64, Physical>,
damage: &[Rectangle<i32, Physical>],
damage: &[Rectangle<i32, Buffer>],
transform: Transform,
alpha: f32,
) -> Result<(), Self::Error> {
@ -1266,7 +1266,7 @@ impl Frame for Gles2Frame {
assert_eq!(mat, mat * transform.invert().matrix());
assert_eq!(transform.matrix(), Matrix3::<f32>::identity());
}
mat = mat * transform.invert().matrix();
mat = mat * transform.matrix();
mat = mat * Matrix3::from_translation(Vector2::new(-0.5, -0.5));
// this matrix should be regular, we can expect invert to succeed
@ -1290,24 +1290,24 @@ impl Frame for Gles2Frame {
let damage = damage
.iter()
.map(|rect| {
let src = src.size.to_f64();
let rect = rect.to_f64();
let rect_constrained_loc = rect
.loc
.constrain(Rectangle::from_extemities((0f64, 0f64), dest.size.to_point()));
let rect_clamped_size = rect.size.clamp(
(0f64, 0f64),
(dest.size.to_point() - rect_constrained_loc).to_size(),
);
.constrain(Rectangle::from_extemities((0f64, 0f64), src.to_point()));
let rect_clamped_size = rect
.size
.clamp((0f64, 0f64), (src.to_point() - rect_constrained_loc).to_size());
let rect = Rectangle::from_loc_and_size(rect_constrained_loc, rect_clamped_size);
let rect_transformed = self.transformation().transform_rect_in(rect, &dest.size);
let rect_transformed = self.transformation().transform_rect_in(rect, &src);
[
(rect_transformed.loc.x / dest.size.w) as f32,
(rect_transformed.loc.y / dest.size.h) as f32,
(rect_transformed.size.w / dest.size.w) as f32,
(rect_transformed.size.h / dest.size.h) as f32,
(rect_transformed.loc.x / src.w) as f32,
(rect_transformed.loc.y / src.h) as f32,
(rect_transformed.size.w / src.w) as f32,
(rect_transformed.size.h / src.h) as f32,
]
})
.flatten()

View File

@ -10,7 +10,7 @@
use std::collections::HashSet;
use std::error::Error;
use crate::utils::{Buffer, Coordinate, Physical, Point, Rectangle, Size};
use crate::utils::{Buffer, Physical, Point, Rectangle, Size, Transform};
#[cfg(feature = "wayland_frontend")]
use crate::wayland::compositor::SurfaceData;
@ -35,27 +35,6 @@ use crate::backend::egl::{
#[cfg(feature = "wayland_frontend")]
pub mod utils;
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
/// Possible transformations to two-dimensional planes
pub enum Transform {
/// Identity transformation (plane is unaltered when applied)
Normal,
/// Plane is rotated by 90 degrees
_90,
/// Plane is rotated by 180 degrees
_180,
/// Plane is rotated by 270 degrees
_270,
/// Plane is flipped vertically
Flipped,
/// Plane is flipped vertically and rotated by 90 degrees
Flipped90,
/// Plane is flipped vertically and rotated by 180 degrees
Flipped180,
/// Plane is flipped vertically and rotated by 270 degrees
Flipped270,
}
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
/// Texture filtering methods
pub enum TextureFilter {
@ -74,70 +53,11 @@ impl Transform {
Transform::_180 => Matrix3::new(-1.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 1.0),
Transform::_270 => Matrix3::new(0.0, 1.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, 1.0),
Transform::Flipped => Matrix3::new(-1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0),
Transform::Flipped90 => Matrix3::new(0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0),
Transform::Flipped90 => Matrix3::new(0.0, -1.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, 1.0),
Transform::Flipped180 => Matrix3::new(1.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 1.0),
Transform::Flipped270 => Matrix3::new(0.0, -1.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, 1.0),
Transform::Flipped270 => Matrix3::new(0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0),
}
}
/// Inverts any 90-degree transformation into 270-degree transformations and vise versa.
///
/// Flipping is preserved and 180/Normal transformation are uneffected.
pub fn invert(&self) -> Transform {
match self {
Transform::Normal => Transform::Normal,
Transform::Flipped => Transform::Flipped,
Transform::_90 => Transform::_270,
Transform::_180 => Transform::_180,
Transform::_270 => Transform::_90,
Transform::Flipped90 => Transform::Flipped270,
Transform::Flipped180 => Transform::Flipped180,
Transform::Flipped270 => Transform::Flipped90,
}
}
/// Transformed size after applying this transformation.
pub fn transform_size<N: Coordinate, Kind>(&self, size: Size<N, Kind>) -> Size<N, Kind> {
if *self == Transform::_90
|| *self == Transform::_270
|| *self == Transform::Flipped90
|| *self == Transform::Flipped270
{
(size.h, size.w).into()
} else {
size
}
}
/// Transforms a rectangle inside an area of a given size by applying this transformation
pub fn transform_rect_in<N: Coordinate, Kind>(
&self,
rect: Rectangle<N, Kind>,
area: &Size<N, Kind>,
) -> Rectangle<N, Kind> {
let size = self.transform_size(rect.size);
let loc = match *self {
Transform::Normal => rect.loc,
Transform::_90 => (area.h - rect.loc.y - rect.size.h, rect.loc.x).into(),
Transform::_180 => (
area.w - rect.loc.x - rect.size.w,
area.h - rect.loc.y - rect.size.h,
)
.into(),
Transform::_270 => (rect.loc.y, area.w - rect.loc.x - rect.size.w).into(),
Transform::Flipped => (area.w - rect.loc.x - rect.size.w, rect.loc.y).into(),
Transform::Flipped90 => (rect.loc.y, rect.loc.x).into(),
Transform::Flipped180 => (rect.loc.x, area.h - rect.loc.y - rect.size.h).into(),
Transform::Flipped270 => (
area.h - rect.loc.y - rect.size.h,
area.w - rect.loc.x - rect.size.w,
)
.into(),
};
Rectangle::from_loc_and_size(loc, size)
}
}
#[cfg(feature = "wayland_frontend")]
@ -217,7 +137,7 @@ pub trait Frame {
texture_scale: i32,
output_scale: f64,
src_transform: Transform,
damage: &[Rectangle<i32, Physical>],
damage: &[Rectangle<i32, Buffer>],
alpha: f32,
) -> Result<(), Self::Error> {
self.render_texture_from_to(
@ -227,7 +147,7 @@ pub trait Frame {
pos,
texture
.size()
.to_logical(texture_scale)
.to_logical(texture_scale, src_transform)
.to_f64()
.to_physical(output_scale),
),
@ -245,7 +165,7 @@ pub trait Frame {
texture: &Self::TextureId,
src: Rectangle<i32, Buffer>,
dst: Rectangle<f64, Physical>,
damage: &[Rectangle<i32, Physical>],
damage: &[Rectangle<i32, Buffer>],
src_transform: Transform,
alpha: f32,
) -> Result<(), Self::Error>;
@ -540,7 +460,7 @@ pub fn buffer_type(buffer: &wl_buffer::WlBuffer) -> Option<BufferType> {
///
/// *Note*: This will only return dimensions for buffer types known to smithay (see [`buffer_type`])
#[cfg(feature = "wayland_frontend")]
pub fn buffer_dimensions(buffer: &wl_buffer::WlBuffer) -> Option<Size<i32, Physical>> {
pub fn buffer_dimensions(buffer: &wl_buffer::WlBuffer) -> Option<Size<i32, Buffer>> {
use crate::backend::allocator::Buffer;
if let Some(buf) = buffer.as_ref().user_data().get::<Dmabuf>() {
@ -560,102 +480,3 @@ pub fn buffer_dimensions(buffer: &wl_buffer::WlBuffer) -> Option<Size<i32, Physi
crate::wayland::shm::with_buffer_contents(buffer, |_, data| (data.width, data.height).into()).ok()
}
#[cfg(test)]
mod tests {
use super::Transform;
use crate::utils::{Logical, Rectangle, Size};
#[test]
fn transform_rect_ident() {
let rect = Rectangle::<i32, Logical>::from_loc_and_size((10, 20), (30, 40));
let size = Size::from((70, 90));
let transform = Transform::Normal;
assert_eq!(rect, transform.transform_rect_in(rect, &size))
}
#[test]
fn transform_rect_90() {
let rect = Rectangle::<i32, Logical>::from_loc_and_size((10, 20), (30, 40));
let size = Size::from((70, 90));
let transform = Transform::_90;
assert_eq!(
Rectangle::from_loc_and_size((30, 10), (40, 30)),
transform.transform_rect_in(rect, &size)
)
}
#[test]
fn transform_rect_180() {
let rect = Rectangle::<i32, Logical>::from_loc_and_size((10, 20), (30, 40));
let size = Size::from((70, 90));
let transform = Transform::_180;
assert_eq!(
Rectangle::from_loc_and_size((30, 30), (30, 40)),
transform.transform_rect_in(rect, &size)
)
}
#[test]
fn transform_rect_270() {
let rect = Rectangle::<i32, Logical>::from_loc_and_size((10, 20), (30, 40));
let size = Size::from((70, 90));
let transform = Transform::_270;
assert_eq!(
Rectangle::from_loc_and_size((20, 30), (40, 30)),
transform.transform_rect_in(rect, &size)
)
}
#[test]
fn transform_rect_f() {
let rect = Rectangle::<i32, Logical>::from_loc_and_size((10, 20), (30, 40));
let size = Size::from((70, 90));
let transform = Transform::Flipped;
assert_eq!(
Rectangle::from_loc_and_size((30, 20), (30, 40)),
transform.transform_rect_in(rect, &size)
)
}
#[test]
fn transform_rect_f90() {
let rect = Rectangle::<i32, Logical>::from_loc_and_size((10, 20), (30, 40));
let size = Size::from((70, 80));
let transform = Transform::Flipped90;
assert_eq!(
Rectangle::from_loc_and_size((20, 10), (40, 30)),
transform.transform_rect_in(rect, &size)
)
}
#[test]
fn transform_rect_f180() {
let rect = Rectangle::<i32, Logical>::from_loc_and_size((10, 20), (30, 40));
let size = Size::from((70, 90));
let transform = Transform::Flipped180;
assert_eq!(
Rectangle::from_loc_and_size((10, 30), (30, 40)),
transform.transform_rect_in(rect, &size)
)
}
#[test]
fn transform_rect_f270() {
let rect = Rectangle::<i32, Logical>::from_loc_and_size((10, 20), (30, 40));
let size = Size::from((70, 90));
let transform = Transform::Flipped270;
assert_eq!(
Rectangle::from_loc_and_size((30, 30), (40, 30)),
transform.transform_rect_in(rect, &size)
)
}
}

View File

@ -2,7 +2,7 @@
use crate::{
backend::renderer::{buffer_dimensions, Frame, ImportAll, Renderer, Texture},
utils::{Logical, Physical, Point, Rectangle, Size},
utils::{Buffer, Logical, Point, Rectangle, Size, Transform},
wayland::compositor::{
is_sync_subsurface, with_surface_tree_upward, BufferAssignment, Damage, SubsurfaceCachedState,
SurfaceAttributes, TraversalAction,
@ -15,8 +15,9 @@ use wayland_server::protocol::{wl_buffer::WlBuffer, wl_surface::WlSurface};
#[derive(Default)]
pub(crate) struct SurfaceState {
pub(crate) buffer_dimensions: Option<Size<i32, Physical>>,
pub(crate) buffer_dimensions: Option<Size<i32, Buffer>>,
pub(crate) buffer_scale: i32,
pub(crate) buffer_transform: Transform,
pub(crate) buffer: Option<WlBuffer>,
pub(crate) texture: Option<Box<dyn std::any::Any + 'static>>,
#[cfg(feature = "desktop")]
@ -30,6 +31,7 @@ impl SurfaceState {
// new contents
self.buffer_dimensions = buffer_dimensions(&buffer);
self.buffer_scale = attrs.buffer_scale;
self.buffer_transform = attrs.buffer_transform.into();
if let Some(old_buffer) = std::mem::replace(&mut self.buffer, Some(buffer)) {
if &old_buffer != self.buffer.as_ref().unwrap() {
old_buffer.release();
@ -52,6 +54,13 @@ impl SurfaceState {
None => {}
}
}
/// Returns the size of the surface.
pub fn surface_size(&self) -> Option<Size<i32, Logical>> {
self.buffer_dimensions
.as_ref()
.map(|dim| dim.to_logical(self.buffer_scale, self.buffer_transform))
}
}
/// Handler to let smithay take over buffer management.
@ -110,10 +119,6 @@ where
T: Texture + 'static,
{
let mut result = Ok(());
let damage = damage
.iter()
.map(|geo| geo.to_f64().to_physical(scale).to_i32_up())
.collect::<Vec<_>>();
with_surface_tree_upward(
surface,
location,
@ -125,17 +130,20 @@ where
// Import a new buffer if necessary
if data.texture.is_none() {
if let Some(buffer) = data.buffer.as_ref() {
let damage = attributes
let buffer_damage = attributes
.damage
.iter()
.map(|dmg| match dmg {
Damage::Buffer(rect) => *rect,
// TODO also apply transformations
Damage::Surface(rect) => rect.to_buffer(attributes.buffer_scale),
Damage::Surface(rect) => rect.to_buffer(
attributes.buffer_scale,
attributes.buffer_transform.into(),
&data.surface_size().unwrap(),
),
})
.collect::<Vec<_>>();
match renderer.import_buffer(buffer, Some(states), &damage) {
match renderer.import_buffer(buffer, Some(states), &buffer_damage) {
Some(Ok(m)) => {
data.texture = Some(Box::new(m));
}
@ -169,10 +177,12 @@ where
let mut location = *location;
if let Some(data) = states.data_map.get::<RefCell<SurfaceState>>() {
let mut data = data.borrow_mut();
let dimensions = data.surface_size();
let buffer_scale = data.buffer_scale;
let buffer_dimensions = data.buffer_dimensions;
let buffer_transform = data.buffer_transform;
let attributes = states.cached_state.current::<SurfaceAttributes>();
if let Some(texture) = data.texture.as_mut().and_then(|x| x.downcast_mut::<T>()) {
let dimensions = dimensions.unwrap();
// we need to re-extract the subsurface offset, as the previous closure
// only passes it to our children
let mut surface_offset = (0, 0).into();
@ -182,23 +192,19 @@ where
location += current.location;
}
let rect = Rectangle::<i32, Physical>::from_loc_and_size(
surface_offset.to_f64().to_physical(scale).to_i32_round(),
buffer_dimensions
.unwrap_or_default()
.to_logical(buffer_scale)
.to_f64()
.to_physical(scale)
.to_i32_round(),
);
let new_damage = damage
let damage = damage
.iter()
.cloned()
.flat_map(|geo| geo.intersection(rect))
// first move the damage by the surface offset in logical space
.map(|mut geo| {
geo.loc -= rect.loc;
// make the damage relative to the surfaec
geo.loc -= surface_offset;
geo
})
// then clamp to surface size again in logical space
.flat_map(|geo| geo.intersection(Rectangle::from_loc_and_size((0, 0), dimensions)))
// lastly transform it into buffer space
.map(|geo| geo.to_buffer(buffer_scale, buffer_transform, &dimensions))
.collect::<Vec<_>>();
// TODO: Take wp_viewporter into account
@ -208,7 +214,7 @@ where
buffer_scale,
scale,
attributes.buffer_transform.into(),
&new_damage,
&damage,
1.0,
) {
result = Err(err);

View File

@ -2,12 +2,12 @@
//! rendering helpers to add custom elements or different clients to a space.
use crate::{
backend::renderer::{utils::SurfaceState, Frame, ImportAll, Renderer, Transform},
backend::renderer::{utils::SurfaceState, Frame, ImportAll, Renderer},
desktop::{
layer::{layer_map_for_output, LayerSurface},
window::Window,
},
utils::{Logical, Point, Rectangle},
utils::{Logical, Point, Rectangle, Transform},
wayland::{
compositor::{
get_parent, is_sync_subsurface, with_surface_tree_downward, SubsurfaceCachedState,
@ -372,7 +372,7 @@ impl Space {
|wl_surface, states, &loc| {
let data = states.data_map.get::<RefCell<SurfaceState>>();
if let Some(size) = data.and_then(|d| d.borrow().size()) {
if let Some(size) = data.and_then(|d| d.borrow().surface_size()) {
let surface_rectangle = Rectangle { loc, size };
if output_geometry.overlaps(surface_rectangle) {

View File

@ -3,7 +3,7 @@
use crate::{
backend::renderer::utils::SurfaceState,
desktop::Space,
utils::{Logical, Point, Rectangle, Size},
utils::{Logical, Point, Rectangle},
wayland::{
compositor::{
with_surface_tree_downward, with_surface_tree_upward, Damage, SubsurfaceCachedState,
@ -17,15 +17,9 @@ use wayland_server::protocol::wl_surface;
use std::cell::RefCell;
impl SurfaceState {
/// Returns the size of the surface.
pub fn size(&self) -> Option<Size<i32, Logical>> {
self.buffer_dimensions
.map(|dims| dims.to_logical(self.buffer_scale))
}
fn contains_point<P: Into<Point<f64, Logical>>>(&self, attrs: &SurfaceAttributes, point: P) -> bool {
let point = point.into();
let size = match self.size() {
let size = match self.surface_size() {
None => return false, // If the surface has no size, it can't have an input region.
Some(size) => size,
};
@ -71,7 +65,7 @@ where
let mut loc = *loc;
let data = states.data_map.get::<RefCell<SurfaceState>>();
if let Some(size) = data.and_then(|d| d.borrow().size()) {
if let Some(size) = data.and_then(|d| d.borrow().surface_size()) {
if states.role == Some("subsurface") {
let current = states.cached_state.current::<SubsurfaceCachedState>();
loc += current.location;
@ -148,7 +142,11 @@ where
damage.extend(attributes.damage.iter().map(|dmg| {
let mut rect = match dmg {
Damage::Buffer(rect) => rect.to_logical(attributes.buffer_scale),
Damage::Buffer(rect) => rect.to_logical(
attributes.buffer_scale,
attributes.buffer_transform.into(),
&data.buffer_dimensions.unwrap(),
),
Damage::Surface(rect) => *rect,
};
rect.loc += location;

View File

@ -406,10 +406,11 @@ impl<N: Coordinate> Point<N, Logical> {
#[inline]
/// Convert this logical point to buffer coordinate space according to given scale factor
pub fn to_buffer(self, scale: N) -> Point<N, Buffer> {
pub fn to_buffer(self, scale: N, transformation: Transform, area: &Size<N, Logical>) -> Point<N, Buffer> {
let point = transformation.transform_point_in(self, area);
Point {
x: self.x.upscale(scale),
y: self.y.upscale(scale),
x: point.x.upscale(scale),
y: point.y.upscale(scale),
_kind: std::marker::PhantomData,
}
}
@ -430,10 +431,11 @@ impl<N: Coordinate> Point<N, Physical> {
impl<N: Coordinate> Point<N, Buffer> {
#[inline]
/// Convert this physical point to logical coordinate space according to given scale factor
pub fn to_logical(self, scale: N) -> Point<N, Logical> {
pub fn to_logical(self, scale: N, transform: Transform, area: &Size<N, Buffer>) -> Point<N, Logical> {
let point = transform.invert().transform_point_in(self, area);
Point {
x: self.x.downscale(scale),
y: self.y.downscale(scale),
x: point.x.downscale(scale),
y: point.y.downscale(scale),
_kind: std::marker::PhantomData,
}
}
@ -667,12 +669,12 @@ impl<N: Coordinate> Size<N, Logical> {
#[inline]
/// Convert this logical size to buffer coordinate space according to given scale factor
pub fn to_buffer(self, scale: N) -> Size<N, Buffer> {
Size {
pub fn to_buffer(self, scale: N, transformation: Transform) -> Size<N, Buffer> {
transformation.transform_size(Size {
w: self.w.upscale(scale),
h: self.h.upscale(scale),
_kind: std::marker::PhantomData,
}
})
}
}
@ -691,12 +693,12 @@ impl<N: Coordinate> Size<N, Physical> {
impl<N: Coordinate> Size<N, Buffer> {
#[inline]
/// Convert this physical point to logical coordinate space according to given scale factor
pub fn to_logical(self, scale: N) -> Size<N, Logical> {
Size {
pub fn to_logical(self, scale: N, transformation: Transform) -> Size<N, Logical> {
transformation.invert().transform_size(Size {
w: self.w.downscale(scale),
h: self.h.downscale(scale),
_kind: std::marker::PhantomData,
}
})
}
}
@ -969,10 +971,24 @@ impl<N: Coordinate> Rectangle<N, Logical> {
/// Convert this logical rectangle to buffer coordinate space according to given scale factor
#[inline]
pub fn to_buffer(self, scale: N) -> Rectangle<N, Buffer> {
pub fn to_buffer(
self,
scale: N,
transformation: Transform,
area: &Size<N, Logical>,
) -> Rectangle<N, Buffer> {
let rect = transformation.transform_rect_in(self, area);
Rectangle {
loc: self.loc.to_buffer(scale),
size: self.size.to_buffer(scale),
loc: Point {
x: rect.loc.x.upscale(scale),
y: rect.loc.y.upscale(scale),
_kind: std::marker::PhantomData,
},
size: Size {
w: rect.size.w.upscale(scale),
h: rect.size.h.upscale(scale),
_kind: std::marker::PhantomData,
},
}
}
}
@ -991,10 +1007,24 @@ impl<N: Coordinate> Rectangle<N, Physical> {
impl<N: Coordinate> Rectangle<N, Buffer> {
/// Convert this physical rectangle to logical coordinate space according to given scale factor
#[inline]
pub fn to_logical(self, scale: N) -> Rectangle<N, Logical> {
pub fn to_logical(
self,
scale: N,
transformation: Transform,
area: &Size<N, Buffer>,
) -> Rectangle<N, Logical> {
let rect = transformation.invert().transform_rect_in(self, area);
Rectangle {
loc: self.loc.to_logical(scale),
size: self.size.to_logical(scale),
loc: Point {
x: rect.loc.x.downscale(scale),
y: rect.loc.y.downscale(scale),
_kind: std::marker::PhantomData,
},
size: Size {
w: rect.size.w.downscale(scale),
h: rect.size.h.downscale(scale),
_kind: std::marker::PhantomData,
},
}
}
}
@ -1071,3 +1101,207 @@ impl<N: Default, Kind> Default for Rectangle<N, Kind> {
}
}
}
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
/// Possible transformations to two-dimensional planes
pub enum Transform {
/// Identity transformation (plane is unaltered when applied)
Normal,
/// Plane is rotated by 90 degrees
_90,
/// Plane is rotated by 180 degrees
_180,
/// Plane is rotated by 270 degrees
_270,
/// Plane is flipped vertically
Flipped,
/// Plane is flipped vertically and rotated by 90 degrees
Flipped90,
/// Plane is flipped vertically and rotated by 180 degrees
Flipped180,
/// Plane is flipped vertically and rotated by 270 degrees
Flipped270,
}
impl Default for Transform {
fn default() -> Transform {
Transform::Normal
}
}
impl Transform {
/// Inverts any 90-degree transformation into 270-degree transformations and vise versa.
///
/// Flipping is preserved and 180/Normal transformation are uneffected.
pub fn invert(&self) -> Transform {
match self {
Transform::Normal => Transform::Normal,
Transform::Flipped => Transform::Flipped,
Transform::_90 => Transform::_270,
Transform::_180 => Transform::_180,
Transform::_270 => Transform::_90,
Transform::Flipped90 => Transform::Flipped270,
Transform::Flipped180 => Transform::Flipped180,
Transform::Flipped270 => Transform::Flipped90,
}
}
/// Transforms a point inside an area of a given size by applying this transformation.
pub fn transform_point_in<N: Coordinate, Kind>(
&self,
point: Point<N, Kind>,
area: &Size<N, Kind>,
) -> Point<N, Kind> {
match *self {
Transform::Normal => point,
Transform::_90 => (area.h - point.y, point.x).into(),
Transform::_180 => (area.w - point.x, area.h - point.y).into(),
Transform::_270 => (point.y, area.w - point.x).into(),
Transform::Flipped => (area.w - point.x, point.y).into(),
Transform::Flipped90 => (point.y, point.x).into(),
Transform::Flipped180 => (point.x, area.h - point.y).into(),
Transform::Flipped270 => (area.h - point.y, area.w - point.x).into(),
}
}
/// Transformed size after applying this transformation.
pub fn transform_size<N: Coordinate, Kind>(&self, size: Size<N, Kind>) -> Size<N, Kind> {
if *self == Transform::_90
|| *self == Transform::_270
|| *self == Transform::Flipped90
|| *self == Transform::Flipped270
{
(size.h, size.w).into()
} else {
size
}
}
/// Transforms a rectangle inside an area of a given size by applying this transformation.
pub fn transform_rect_in<N: Coordinate, Kind>(
&self,
rect: Rectangle<N, Kind>,
area: &Size<N, Kind>,
) -> Rectangle<N, Kind> {
let size = self.transform_size(rect.size);
let loc = match *self {
Transform::Normal => rect.loc,
Transform::_90 => (area.h - rect.loc.y - rect.size.h, rect.loc.x).into(),
Transform::_180 => (
area.w - rect.loc.x - rect.size.w,
area.h - rect.loc.y - rect.size.h,
)
.into(),
Transform::_270 => (rect.loc.y, area.w - rect.loc.x - rect.size.w).into(),
Transform::Flipped => (area.w - rect.loc.x - rect.size.w, rect.loc.y).into(),
Transform::Flipped90 => (rect.loc.y, rect.loc.x).into(),
Transform::Flipped180 => (rect.loc.x, area.h - rect.loc.y - rect.size.h).into(),
Transform::Flipped270 => (
area.h - rect.loc.y - rect.size.h,
area.w - rect.loc.x - rect.size.w,
)
.into(),
};
Rectangle::from_loc_and_size(loc, size)
}
}
#[cfg(test)]
mod tests {
use super::{Logical, Rectangle, Size, Transform};
#[test]
fn transform_rect_ident() {
let rect = Rectangle::<i32, Logical>::from_loc_and_size((10, 20), (30, 40));
let size = Size::from((70, 90));
let transform = Transform::Normal;
assert_eq!(rect, transform.transform_rect_in(rect, &size))
}
#[test]
fn transform_rect_90() {
let rect = Rectangle::<i32, Logical>::from_loc_and_size((10, 20), (30, 40));
let size = Size::from((70, 90));
let transform = Transform::_90;
assert_eq!(
Rectangle::from_loc_and_size((30, 10), (40, 30)),
transform.transform_rect_in(rect, &size)
)
}
#[test]
fn transform_rect_180() {
let rect = Rectangle::<i32, Logical>::from_loc_and_size((10, 20), (30, 40));
let size = Size::from((70, 90));
let transform = Transform::_180;
assert_eq!(
Rectangle::from_loc_and_size((30, 30), (30, 40)),
transform.transform_rect_in(rect, &size)
)
}
#[test]
fn transform_rect_270() {
let rect = Rectangle::<i32, Logical>::from_loc_and_size((10, 20), (30, 40));
let size = Size::from((70, 90));
let transform = Transform::_270;
assert_eq!(
Rectangle::from_loc_and_size((20, 30), (40, 30)),
transform.transform_rect_in(rect, &size)
)
}
#[test]
fn transform_rect_f() {
let rect = Rectangle::<i32, Logical>::from_loc_and_size((10, 20), (30, 40));
let size = Size::from((70, 90));
let transform = Transform::Flipped;
assert_eq!(
Rectangle::from_loc_and_size((30, 20), (30, 40)),
transform.transform_rect_in(rect, &size)
)
}
#[test]
fn transform_rect_f90() {
let rect = Rectangle::<i32, Logical>::from_loc_and_size((10, 20), (30, 40));
let size = Size::from((70, 80));
let transform = Transform::Flipped90;
assert_eq!(
Rectangle::from_loc_and_size((20, 10), (40, 30)),
transform.transform_rect_in(rect, &size)
)
}
#[test]
fn transform_rect_f180() {
let rect = Rectangle::<i32, Logical>::from_loc_and_size((10, 20), (30, 40));
let size = Size::from((70, 90));
let transform = Transform::Flipped180;
assert_eq!(
Rectangle::from_loc_and_size((10, 30), (30, 40)),
transform.transform_rect_in(rect, &size)
)
}
#[test]
fn transform_rect_f270() {
let rect = Rectangle::<i32, Logical>::from_loc_and_size((10, 20), (30, 40));
let size = Size::from((70, 90));
let transform = Transform::Flipped270;
assert_eq!(
Rectangle::from_loc_and_size((30, 30), (40, 30)),
transform.transform_rect_in(rect, &size)
)
}
}

View File

@ -10,7 +10,7 @@ pub mod x11rb;
pub(crate) mod ids;
pub mod user_data;
pub use self::geometry::{Buffer, Coordinate, Logical, Physical, Point, Raw, Rectangle, Size};
pub use self::geometry::{Buffer, Coordinate, Logical, Physical, Point, Raw, Rectangle, Size, Transform};
/// This resource is not managed by Smithay
#[derive(Debug)]