initial commit
This commit is contained in:
		
						commit
						b647d95713
					
				
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1 @@ | ||||||
|  | /target | ||||||
							
								
								
									
										18
									
								
								Cargo.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								Cargo.toml
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,18 @@ | ||||||
|  | [package] | ||||||
|  | name = "grisly" | ||||||
|  | version = "0.1.0" | ||||||
|  | edition = "2021" | ||||||
|  | 
 | ||||||
|  | [dependencies] | ||||||
|  | anyhow = "1.0.93" | ||||||
|  | bytemuck = "1.20.0" | ||||||
|  | clap = "4.5.21" | ||||||
|  | env_logger = "0.11.5" | ||||||
|  | image = "0.25.5" | ||||||
|  | libc = "0.2.165" | ||||||
|  | log = "0.4.22" | ||||||
|  | rand = "0.8.5" | ||||||
|  | thiserror = "2.0.3" | ||||||
|  | wayland-client = "0.31.7" | ||||||
|  | wayland-protocols = { version = "0.32.5", features = ["client", "unstable"] } | ||||||
|  | wayland-protocols-wlr = {version = "0.3.5", features = ["client"]} | ||||||
							
								
								
									
										940
									
								
								src/main.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										940
									
								
								src/main.rs
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,940 @@ | ||||||
|  | use std::{ | ||||||
|  |     collections::HashMap, | ||||||
|  |     ffi::CString, | ||||||
|  |     io::BufWriter, | ||||||
|  |     os::fd::{AsFd, AsRawFd, FromRawFd}, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | use image::{math::Rect, GenericImage, GenericImageView}; | ||||||
|  | use rand::Rng; | ||||||
|  | use wayland_client::{ | ||||||
|  |     backend::ObjectId, | ||||||
|  |     globals::{registry_queue_init, GlobalListContents}, | ||||||
|  |     protocol::{ | ||||||
|  |         wl_buffer::WlBuffer, | ||||||
|  |         wl_output::{self, Mode, Transform, WlOutput}, | ||||||
|  |         wl_registry::{self, WlRegistry}, | ||||||
|  |         wl_shm::{self, WlShm}, | ||||||
|  |         wl_shm_pool::WlShmPool, | ||||||
|  |     }, | ||||||
|  |     Connection, Dispatch, Proxy, QueueHandle, WEnum, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | use wayland_protocols::xdg::xdg_output::zv1::client::{ | ||||||
|  |     zxdg_output_manager_v1::ZxdgOutputManagerV1, zxdg_output_v1::ZxdgOutputV1, | ||||||
|  | }; | ||||||
|  | use wayland_protocols_wlr::screencopy::v1::client::{ | ||||||
|  |     zwlr_screencopy_frame_v1::{self, ZwlrScreencopyFrameV1}, | ||||||
|  |     zwlr_screencopy_manager_v1::ZwlrScreencopyManagerV1, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, Clone, Copy)] | ||||||
|  | struct BufferInfo { | ||||||
|  |     format: wl_shm::Format, | ||||||
|  |     width: u32, | ||||||
|  |     height: u32, | ||||||
|  |     stride: u32, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, PartialEq, Eq)] | ||||||
|  | enum FrameState { | ||||||
|  |     Pre, | ||||||
|  |     BufferDone, | ||||||
|  |     Copied, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug)] | ||||||
|  | enum FrameData { | ||||||
|  |     Pre(Vec<BufferInfo>), | ||||||
|  |     Buffer(BufferInfo), | ||||||
|  |     Copying { | ||||||
|  |         buffer: WlBuffer, | ||||||
|  |         info: BufferInfo, | ||||||
|  |         offset: usize, | ||||||
|  |     }, | ||||||
|  |     Copied { | ||||||
|  |         buffer: WlBuffer, | ||||||
|  |         info: BufferInfo, | ||||||
|  |         offset: usize, | ||||||
|  |     }, | ||||||
|  |     Done, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl FrameData { | ||||||
|  |     fn select_buffer(&mut self) { | ||||||
|  |         if let Self::Pre(infos) = self { | ||||||
|  |             *self = Self::Buffer(infos.pop().unwrap()); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug)] | ||||||
|  | struct Frame { | ||||||
|  |     frame: ZwlrScreencopyFrameV1, | ||||||
|  |     buffer_infos: Vec<BufferInfo>, | ||||||
|  |     inverted_y: bool, | ||||||
|  |     data: FrameData, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug)] | ||||||
|  | struct Output { | ||||||
|  |     output: WlOutput, | ||||||
|  |     xdg_output: ZxdgOutputV1, | ||||||
|  |     logical_size: Option<(i32, i32)>, | ||||||
|  |     physical_size: Option<(i32, i32)>, | ||||||
|  |     logical_position: Option<(i32, i32)>, | ||||||
|  |     physical_position: Option<(i32, i32)>, | ||||||
|  |     transform: Option<Transform>, | ||||||
|  |     scale: Option<i32>, | ||||||
|  |     frame: Frame, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Output { | ||||||
|  |     fn bind< | ||||||
|  |         U: Send + Sync + 'static, | ||||||
|  |         D: Dispatch<WlOutput, U> | ||||||
|  |             + Dispatch<ZxdgOutputV1, ObjectId> | ||||||
|  |             + Dispatch<ZwlrScreencopyFrameV1, ObjectId> | ||||||
|  |             + 'static, | ||||||
|  |     >( | ||||||
|  |         registry: &WlRegistry, | ||||||
|  |         name: u32, | ||||||
|  |         version: u32, | ||||||
|  |         qh: &QueueHandle<D>, | ||||||
|  |         output_data: U, | ||||||
|  |         xdg_manager: &ZxdgOutputManagerV1, | ||||||
|  |         screencopy: &ZwlrScreencopyManagerV1, | ||||||
|  |     ) -> Self { | ||||||
|  |         let output = registry.bind(name, version, &qh, output_data); | ||||||
|  |         let xdg_output = xdg_manager.get_xdg_output(&output, &qh, output.id()); | ||||||
|  |         let frame = screencopy.capture_output(0, &output, &qh, output.id()); | ||||||
|  |         Output { | ||||||
|  |             output, | ||||||
|  |             xdg_output, | ||||||
|  |             logical_size: None, | ||||||
|  |             physical_position: None, | ||||||
|  |             transform: None, | ||||||
|  |             scale: Some(1), | ||||||
|  |             logical_position: None, | ||||||
|  |             physical_size: None, | ||||||
|  |             frame: Frame { | ||||||
|  |                 frame, | ||||||
|  |                 inverted_y: false, | ||||||
|  |                 buffer_infos: Vec::new(), | ||||||
|  |                 data: FrameData::Pre(vec![]), | ||||||
|  |             }, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug)] | ||||||
|  | struct App { | ||||||
|  |     screencopy_mgr: ZwlrScreencopyManagerV1, | ||||||
|  |     xdg_manager: ZxdgOutputManagerV1, | ||||||
|  |     outputs: HashMap<ObjectId, Output>, | ||||||
|  |     shm: WlShm, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Dispatch<ZwlrScreencopyManagerV1, ()> for App { | ||||||
|  |     fn event( | ||||||
|  |         _state: &mut Self, | ||||||
|  |         _proxy: &ZwlrScreencopyManagerV1, | ||||||
|  |         _event: <ZwlrScreencopyManagerV1 as Proxy>::Event, | ||||||
|  |         _data: &(), | ||||||
|  |         _conn: &Connection, | ||||||
|  |         _qhandle: &wayland_client::QueueHandle<Self>, | ||||||
|  |     ) { | ||||||
|  |         // this does nothing
 | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Dispatch<ZwlrScreencopyFrameV1, ObjectId> for App { | ||||||
|  |     fn event( | ||||||
|  |         state: &mut Self, | ||||||
|  |         proxy: &ZwlrScreencopyFrameV1, | ||||||
|  |         event: <ZwlrScreencopyFrameV1 as Proxy>::Event, | ||||||
|  |         data: &ObjectId, | ||||||
|  |         _conn: &Connection, | ||||||
|  |         _qhandle: &wayland_client::QueueHandle<Self>, | ||||||
|  |     ) { | ||||||
|  |         let Some(output) = state.outputs.get_mut(data) else { | ||||||
|  |             return; | ||||||
|  |         }; | ||||||
|  |         match event { | ||||||
|  |             zwlr_screencopy_frame_v1::Event::Buffer { | ||||||
|  |                 format, | ||||||
|  |                 width, | ||||||
|  |                 height, | ||||||
|  |                 stride, | ||||||
|  |             } => { | ||||||
|  |                 if let FrameData::Pre(ref mut infos) = output.frame.data { | ||||||
|  |                     infos.push(BufferInfo { | ||||||
|  |                         format: format.into_result().unwrap(), | ||||||
|  |                         width, | ||||||
|  |                         height, | ||||||
|  |                         stride, | ||||||
|  |                     }); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             zwlr_screencopy_frame_v1::Event::Flags { flags } => { | ||||||
|  |                 output.frame.inverted_y = flags | ||||||
|  |                     .into_result() | ||||||
|  |                     .unwrap() | ||||||
|  |                     .contains(zwlr_screencopy_frame_v1::Flags::YInvert); | ||||||
|  |             } | ||||||
|  |             zwlr_screencopy_frame_v1::Event::Ready { .. } => { | ||||||
|  |                 if let FrameData::Copying { | ||||||
|  |                     buffer, | ||||||
|  |                     info, | ||||||
|  |                     offset, | ||||||
|  |                 } = core::mem::replace(&mut output.frame.data, FrameData::Done) | ||||||
|  |                 { | ||||||
|  |                     output.frame.data = FrameData::Copied { | ||||||
|  |                         buffer, | ||||||
|  |                         info, | ||||||
|  |                         offset, | ||||||
|  |                     }; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             zwlr_screencopy_frame_v1::Event::Failed => { | ||||||
|  |                 output.frame.data = FrameData::Done; | ||||||
|  |                 panic!("screencopy failed.") | ||||||
|  |             } | ||||||
|  |             zwlr_screencopy_frame_v1::Event::BufferDone => { | ||||||
|  |                 // FIXME: pick best buffer format
 | ||||||
|  |                 // proxy.copy(buffer)
 | ||||||
|  |                 if let FrameData::Pre(infos) = | ||||||
|  |                     core::mem::replace(&mut output.frame.data, FrameData::Done) | ||||||
|  |                 { | ||||||
|  |                     let info = infos | ||||||
|  |                         .iter() | ||||||
|  |                         .max_by_key(|info| match info.format { | ||||||
|  |                             wl_shm::Format::Xrgb8888 => 20, | ||||||
|  |                             wl_shm::Format::Argb8888 => 10, | ||||||
|  |                             _ => 0, | ||||||
|  |                         }) | ||||||
|  |                         .unwrap() | ||||||
|  |                         .clone(); | ||||||
|  |                     output.frame.data = FrameData::Buffer(info); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             _ => {} | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Dispatch<ZxdgOutputManagerV1, ()> for App { | ||||||
|  |     fn event( | ||||||
|  |         state: &mut Self, | ||||||
|  |         proxy: &ZxdgOutputManagerV1, | ||||||
|  |         event: <ZxdgOutputManagerV1 as Proxy>::Event, | ||||||
|  |         data: &(), | ||||||
|  |         conn: &Connection, | ||||||
|  |         qhandle: &wayland_client::QueueHandle<Self>, | ||||||
|  |     ) { | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Dispatch<ZxdgOutputV1, ObjectId> for App { | ||||||
|  |     fn event( | ||||||
|  |         state: &mut Self, | ||||||
|  |         proxy: &ZxdgOutputV1, | ||||||
|  |         event: <ZxdgOutputV1 as Proxy>::Event, | ||||||
|  |         data: &ObjectId, | ||||||
|  |         conn: &Connection, | ||||||
|  |         qhandle: &wayland_client::QueueHandle<Self>, | ||||||
|  |     ) { | ||||||
|  |         let Some(output) = state.outputs.get_mut(data) else { | ||||||
|  |             return; | ||||||
|  |         }; | ||||||
|  |         match event { | ||||||
|  |             wayland_protocols::xdg::xdg_output::zv1::client::zxdg_output_v1::Event::LogicalPosition { x, y } => { | ||||||
|  |                     output.logical_position = Some((x,y)); | ||||||
|  |             }, | ||||||
|  |             wayland_protocols::xdg::xdg_output::zv1::client::zxdg_output_v1::Event::LogicalSize { width, height } => { | ||||||
|  |                 output.logical_size = Some((width, height)); | ||||||
|  |             }, | ||||||
|  |             _ => {}, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Dispatch<wl_output::WlOutput, u32> for App { | ||||||
|  |     fn event( | ||||||
|  |         state: &mut Self, | ||||||
|  |         proxy: &wl_output::WlOutput, | ||||||
|  |         event: <wl_output::WlOutput as Proxy>::Event, | ||||||
|  |         _data: &u32, | ||||||
|  |         _conn: &Connection, | ||||||
|  |         _qhandle: &wayland_client::QueueHandle<Self>, | ||||||
|  |     ) { | ||||||
|  |         let Some(output) = state.outputs.get_mut(&proxy.id()) else { | ||||||
|  |             return; | ||||||
|  |         }; | ||||||
|  |         match event { | ||||||
|  |             wl_output::Event::Geometry { | ||||||
|  |                 x, y, transform, .. | ||||||
|  |             } => { | ||||||
|  |                 output.physical_position = Some((x, y)); | ||||||
|  |                 output.transform = Some(transform.into_result().unwrap()); | ||||||
|  |             } | ||||||
|  |             wl_output::Event::Mode { | ||||||
|  |                 flags, | ||||||
|  |                 width, | ||||||
|  |                 height, | ||||||
|  |                 .. | ||||||
|  |             } => { | ||||||
|  |                 if flags == WEnum::Value(Mode::Current) { | ||||||
|  |                     output.physical_size = Some((width, height)); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             wl_output::Event::Scale { factor } => { | ||||||
|  |                 output.scale = Some(factor); | ||||||
|  |             } | ||||||
|  |             _ => {} | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Dispatch<WlShm, ()> for App { | ||||||
|  |     fn event( | ||||||
|  |         state: &mut Self, | ||||||
|  |         proxy: &WlShm, | ||||||
|  |         event: <WlShm as Proxy>::Event, | ||||||
|  |         data: &(), | ||||||
|  |         conn: &Connection, | ||||||
|  |         qhandle: &wayland_client::QueueHandle<Self>, | ||||||
|  |     ) { | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Dispatch<WlShmPool, ()> for App { | ||||||
|  |     fn event( | ||||||
|  |         _state: &mut Self, | ||||||
|  |         _proxy: &WlShmPool, | ||||||
|  |         event: <WlShmPool as Proxy>::Event, | ||||||
|  |         _data: &(), | ||||||
|  |         _conn: &Connection, | ||||||
|  |         _qhandle: &wayland_client::QueueHandle<Self>, | ||||||
|  |     ) { | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Dispatch<WlBuffer, ()> for App { | ||||||
|  |     fn event( | ||||||
|  |         _state: &mut Self, | ||||||
|  |         _proxy: &WlBuffer, | ||||||
|  |         event: <WlBuffer as Proxy>::Event, | ||||||
|  |         _data: &(), | ||||||
|  |         _conn: &Connection, | ||||||
|  |         _qhandle: &wayland_client::QueueHandle<Self>, | ||||||
|  |     ) { | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Dispatch<wl_registry::WlRegistry, GlobalListContents> for App { | ||||||
|  |     fn event( | ||||||
|  |         state: &mut Self, | ||||||
|  |         registry: &wl_registry::WlRegistry, | ||||||
|  |         event: <wl_registry::WlRegistry as Proxy>::Event, | ||||||
|  |         globals: &GlobalListContents, | ||||||
|  |         _conn: &Connection, | ||||||
|  |         qh: &wayland_client::QueueHandle<Self>, | ||||||
|  |     ) { | ||||||
|  |         match event { | ||||||
|  |             wl_registry::Event::Global { name, version, .. } => { | ||||||
|  |                 let output = Output::bind( | ||||||
|  |                     registry, | ||||||
|  |                     name, | ||||||
|  |                     version, | ||||||
|  |                     qh, | ||||||
|  |                     name, | ||||||
|  |                     &state.xdg_manager, | ||||||
|  |                     &state.screencopy_mgr, | ||||||
|  |                 ); | ||||||
|  |                 state.outputs.insert(output.output.id(), output); | ||||||
|  |             } | ||||||
|  |             wl_registry::Event::GlobalRemove { name } => globals.with_list(|globals| { | ||||||
|  |                 if globals | ||||||
|  |                     .iter() | ||||||
|  |                     .find(|global| global.name == name) | ||||||
|  |                     .map(|global| &global.interface == wl_output::WlOutput::interface().name) | ||||||
|  |                     == Some(true) | ||||||
|  |                 { | ||||||
|  |                     state | ||||||
|  |                         .outputs | ||||||
|  |                         .retain(|_, output| output.output.data() != Some(&name)); | ||||||
|  |                 } | ||||||
|  |             }), | ||||||
|  |             _ => {} | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | struct Shm { | ||||||
|  |     fd: std::os::unix::io::OwnedFd, | ||||||
|  |     name: CString, | ||||||
|  |     size: usize, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Shm { | ||||||
|  |     fn new(size: usize) -> std::io::Result<Self> { | ||||||
|  |         let name = CString::new(format!( | ||||||
|  |             "grisly-{}", | ||||||
|  |             rand::thread_rng() | ||||||
|  |                 .sample_iter(&rand::distributions::Alphanumeric) | ||||||
|  |                 .take(8) | ||||||
|  |                 .map(char::from) | ||||||
|  |                 .collect::<String>() | ||||||
|  |         )) | ||||||
|  |         .unwrap(); | ||||||
|  |         let fd = match unsafe { | ||||||
|  |             libc::shm_open( | ||||||
|  |                 name.as_ptr(), | ||||||
|  |                 libc::O_RDWR | libc::O_CREAT | libc::O_EXCL, | ||||||
|  |                 0600, | ||||||
|  |             ) | ||||||
|  |         } { | ||||||
|  |             -1 => Err(std::io::Error::last_os_error()), | ||||||
|  |             fd => Ok(unsafe { std::os::unix::io::OwnedFd::from_raw_fd(fd) }), | ||||||
|  |         }?; | ||||||
|  | 
 | ||||||
|  |         match unsafe { libc::ftruncate(fd.as_raw_fd(), size as i64) } { | ||||||
|  |             -1 => Err(std::io::Error::last_os_error()), | ||||||
|  |             _ => Ok(()), | ||||||
|  |         }?; | ||||||
|  | 
 | ||||||
|  |         Ok(Self { fd, name, size }) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn map<'a>(&'a self) -> std::io::Result<&'a mut [u8]> { | ||||||
|  |         match unsafe { | ||||||
|  |             libc::mmap( | ||||||
|  |                 core::ptr::null_mut(), | ||||||
|  |                 self.size, | ||||||
|  |                 libc::PROT_READ | libc::PROT_WRITE, | ||||||
|  |                 libc::MAP_SHARED, | ||||||
|  |                 self.fd.as_raw_fd(), | ||||||
|  |                 0, | ||||||
|  |             ) | ||||||
|  |         } { | ||||||
|  |             libc::MAP_FAILED => Err(std::io::Error::last_os_error()), | ||||||
|  |             ptr => Ok(unsafe { core::slice::from_raw_parts_mut(ptr as *mut _, self.size) }), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Drop for Shm { | ||||||
|  |     fn drop(&mut self) { | ||||||
|  |         unsafe { | ||||||
|  |             libc::shm_unlink(self.name.as_ptr()); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //
 | ||||||
|  | // Post-Processing transforms & scalings:
 | ||||||
|  | // wl_output gives an integer scale to apply to buffer contents.
 | ||||||
|  | // - question: does this apply after the xdg_output logical size?
 | ||||||
|  | // - we use a sampled image to copy to a differently sized logical size anyway
 | ||||||
|  | //
 | ||||||
|  | // wl_output gives a transform which is applied to buffer contents.
 | ||||||
|  | //
 | ||||||
|  | 
 | ||||||
|  | fn main() { | ||||||
|  |     let conn = Connection::connect_to_env().unwrap(); | ||||||
|  | 
 | ||||||
|  |     let (globals, mut queue) = registry_queue_init(&conn).unwrap(); | ||||||
|  | 
 | ||||||
|  |     let qh = queue.handle(); | ||||||
|  | 
 | ||||||
|  |     let screencopy_mgr = globals | ||||||
|  |         .bind::<ZwlrScreencopyManagerV1, _, _>(&queue.handle(), 1..=3, ()) | ||||||
|  |         .unwrap(); | ||||||
|  |     let xdg_manager = globals | ||||||
|  |         .bind::<ZxdgOutputManagerV1, _, _>(&queue.handle(), 3..=3, ()) | ||||||
|  |         .unwrap(); | ||||||
|  | 
 | ||||||
|  |     let outputs = globals.contents().with_list(|list| { | ||||||
|  |         list.iter() | ||||||
|  |             .filter(|global| &global.interface == wl_output::WlOutput::interface().name) | ||||||
|  |             .map(|global| { | ||||||
|  |                 let output = Output::bind( | ||||||
|  |                     globals.registry(), | ||||||
|  |                     global.name, | ||||||
|  |                     global.version, | ||||||
|  |                     &qh, | ||||||
|  |                     global.name, | ||||||
|  |                     &xdg_manager, | ||||||
|  |                     &screencopy_mgr, | ||||||
|  |                 ); | ||||||
|  | 
 | ||||||
|  |                 (output.output.id(), output) | ||||||
|  |             }) | ||||||
|  |             .collect::<HashMap<_, _>>() | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     let shm = globals.bind(&queue.handle(), 0..=2, ()).unwrap(); | ||||||
|  | 
 | ||||||
|  |     let mut app = App { | ||||||
|  |         outputs, | ||||||
|  |         xdg_manager, | ||||||
|  |         screencopy_mgr, | ||||||
|  |         shm, | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     while !app | ||||||
|  |         .outputs | ||||||
|  |         .iter() | ||||||
|  |         .all(|(_, output)| matches!(output.frame.data, FrameData::Buffer(_))) | ||||||
|  |     { | ||||||
|  |         _ = queue.roundtrip(&mut app).unwrap(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     let mut total_size = 0; | ||||||
|  |     let buffers = app | ||||||
|  |         .outputs | ||||||
|  |         .iter_mut() | ||||||
|  |         .map(|(_, output)| { | ||||||
|  |             let FrameData::Buffer(info) = | ||||||
|  |                 core::mem::replace(&mut output.frame.data, FrameData::Done) | ||||||
|  |             else { | ||||||
|  |                 unreachable!(); | ||||||
|  |             }; | ||||||
|  |             let size = info.height * info.stride; | ||||||
|  |             let offset = total_size; | ||||||
|  |             total_size += size; | ||||||
|  | 
 | ||||||
|  |             (output, info, offset) | ||||||
|  |         }) | ||||||
|  |         .collect::<Vec<_>>(); | ||||||
|  | 
 | ||||||
|  |     let shm = Shm::new(total_size as usize).unwrap(); | ||||||
|  |     let buffer = shm.map().unwrap(); | ||||||
|  | 
 | ||||||
|  |     let pool = app | ||||||
|  |         .shm | ||||||
|  |         .create_pool(shm.fd.as_fd(), total_size as i32, &qh, ()); | ||||||
|  | 
 | ||||||
|  |     for (output, info, offset) in buffers { | ||||||
|  |         let buffer = pool.create_buffer( | ||||||
|  |             offset as i32, | ||||||
|  |             info.width as i32, | ||||||
|  |             info.height as i32, | ||||||
|  |             info.stride as i32, | ||||||
|  |             info.format, | ||||||
|  |             &qh, | ||||||
|  |             (), | ||||||
|  |         ); | ||||||
|  | 
 | ||||||
|  |         output.frame.frame.copy(&buffer); | ||||||
|  | 
 | ||||||
|  |         output.frame.data = FrameData::Copying { | ||||||
|  |             buffer, | ||||||
|  |             info, | ||||||
|  |             offset: offset as usize, | ||||||
|  |         }; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     while !app | ||||||
|  |         .outputs | ||||||
|  |         .iter() | ||||||
|  |         .all(|(_, output)| matches!(output.frame.data, FrameData::Copied { .. })) | ||||||
|  |     { | ||||||
|  |         _ = queue.roundtrip(&mut app).unwrap(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn rect_union(lhs: Rect, rhs: Rect) -> Rect { | ||||||
|  |         struct AABB { | ||||||
|  |             ax: u32, | ||||||
|  |             ay: u32, | ||||||
|  |             bx: u32, | ||||||
|  |             by: u32, | ||||||
|  |         } | ||||||
|  |         let lhs = AABB { | ||||||
|  |             ax: lhs.x, | ||||||
|  |             ay: lhs.y, | ||||||
|  |             bx: lhs.x + lhs.width, | ||||||
|  |             by: lhs.y + lhs.height, | ||||||
|  |         }; | ||||||
|  |         let rhs = AABB { | ||||||
|  |             ax: rhs.x, | ||||||
|  |             ay: rhs.y, | ||||||
|  |             bx: rhs.x + rhs.width, | ||||||
|  |             by: rhs.y + rhs.height, | ||||||
|  |         }; | ||||||
|  |         let union = AABB { | ||||||
|  |             ax: lhs.ax.min(rhs.ax), | ||||||
|  |             ay: lhs.ay.min(rhs.ay), | ||||||
|  |             bx: lhs.bx.max(rhs.bx), | ||||||
|  |             by: lhs.by.max(rhs.by), | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         Rect { | ||||||
|  |             x: union.ax, | ||||||
|  |             y: union.ay, | ||||||
|  |             width: union.bx - union.ax, | ||||||
|  |             height: union.by - union.ay, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     let mut image_rect = Rect { | ||||||
|  |         x: 0, | ||||||
|  |         y: 0, | ||||||
|  |         width: 0, | ||||||
|  |         height: 0, | ||||||
|  |     }; | ||||||
|  |     let buffers = app | ||||||
|  |         .outputs | ||||||
|  |         .values_mut() | ||||||
|  |         .map(|output| { | ||||||
|  |             let FrameData::Copied { info, offset, .. } = | ||||||
|  |                 core::mem::replace(&mut output.frame.data, FrameData::Done) | ||||||
|  |             else { | ||||||
|  |                 unreachable!(); | ||||||
|  |             }; | ||||||
|  | 
 | ||||||
|  |             let (px, py) = output.physical_position.unwrap_or((0, 0)); | ||||||
|  |             let (lx, ly) = output.logical_position.unwrap_or((0, 0)); | ||||||
|  |             // image destination
 | ||||||
|  |             let (x, y) = (lx - px, ly - py); | ||||||
|  | 
 | ||||||
|  |             let (width, height) = output | ||||||
|  |                 .logical_size | ||||||
|  |                 .unwrap_or((info.width as i32, info.height as i32)); | ||||||
|  | 
 | ||||||
|  |             let rect = image::math::Rect { | ||||||
|  |                 x: x as u32, | ||||||
|  |                 y: y as u32, | ||||||
|  |                 width: width as u32, | ||||||
|  |                 height: height as u32, | ||||||
|  |             }; | ||||||
|  | 
 | ||||||
|  |             println!("buffer: {:?}", info); | ||||||
|  |             println!(" - invert-y: {}", output.frame.inverted_y); | ||||||
|  |             println!("destination rect: {:?}", rect); | ||||||
|  |             println!(" - transform: {:?}", output.transform); | ||||||
|  | 
 | ||||||
|  |             image_rect = rect_union(image_rect, rect); | ||||||
|  | 
 | ||||||
|  |             (info, offset, rect, output.frame.inverted_y) | ||||||
|  |         }) | ||||||
|  |         .collect::<Vec<_>>(); | ||||||
|  | 
 | ||||||
|  |     let mut image = image::RgbImage::new(image_rect.width, image_rect.height); | ||||||
|  | 
 | ||||||
|  |     let now = std::time::Instant::now(); | ||||||
|  |     for (info, offset, rect, inverted_y) in buffers { | ||||||
|  |         let size = info.stride * info.height; | ||||||
|  |         let view = BufferView { | ||||||
|  |             info, | ||||||
|  |             inverted_y, | ||||||
|  |             bytes: &buffer[offset..][..size as usize], | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         if rect.width != info.width || rect.height != info.height { | ||||||
|  |             let sampled = SampledView { | ||||||
|  |                 view: &view, | ||||||
|  |                 dimensions: (rect.width, rect.height), | ||||||
|  |             }; | ||||||
|  |             image.copy_from(&sampled, rect.x, rect.y).unwrap(); | ||||||
|  |         } else { | ||||||
|  |             image.copy_from(&view, rect.x, rect.y).unwrap(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     println!("copying: {}s", now.elapsed().as_secs_f64()); | ||||||
|  | 
 | ||||||
|  |     let file = std::fs::File::create("out.png").unwrap(); | ||||||
|  |     let writer = BufWriter::new(file); | ||||||
|  |     let now = std::time::Instant::now(); | ||||||
|  |     image | ||||||
|  |         .write_with_encoder(image::codecs::png::PngEncoder::new_with_quality( | ||||||
|  |             writer, | ||||||
|  |             image::codecs::png::CompressionType::Default, | ||||||
|  |             image::codecs::png::FilterType::Adaptive, | ||||||
|  |         )) | ||||||
|  |         .unwrap(); | ||||||
|  |     println!("encoding: {}s", now.elapsed().as_secs_f64()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | struct SampledView<'a, V: GenericImageView> { | ||||||
|  |     view: &'a V, | ||||||
|  |     dimensions: (u32, u32), | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<'a, V: GenericImageView> GenericImageView for SampledView<'a, V> { | ||||||
|  |     type Pixel = V::Pixel; | ||||||
|  | 
 | ||||||
|  |     fn dimensions(&self) -> (u32, u32) { | ||||||
|  |         self.dimensions | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn get_pixel(&self, x: u32, y: u32) -> Self::Pixel { | ||||||
|  |         let u = x as f32 / self.dimensions.0 as f32; | ||||||
|  |         let v = y as f32 / self.dimensions.1 as f32; | ||||||
|  | 
 | ||||||
|  |         image::imageops::sample_bilinear(self.view, u, v).unwrap() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | struct BufferView<'a> { | ||||||
|  |     info: BufferInfo, | ||||||
|  |     inverted_y: bool, | ||||||
|  |     bytes: &'a [u8], | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl BufferView<'_> { | ||||||
|  |     fn pixel_offset(&self, x: u32, y: u32) -> usize { | ||||||
|  |         let y = if self.inverted_y { | ||||||
|  |             self.info.height - y | ||||||
|  |         } else { | ||||||
|  |             y | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         self.info.stride as usize * y as usize + (x as usize * self.pixel_stride() as usize) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn pixel_stride(&self) -> u32 { | ||||||
|  |         (match self.info.format { | ||||||
|  |             wl_shm::Format::Argb8888 => 32, | ||||||
|  |             wl_shm::Format::Xrgb8888 => 32, | ||||||
|  |             wl_shm::Format::C8 => 8, | ||||||
|  |             wl_shm::Format::Rgb332 => 8, | ||||||
|  |             wl_shm::Format::Bgr233 => 8, | ||||||
|  |             wl_shm::Format::Xrgb4444 => 16, | ||||||
|  |             wl_shm::Format::Xbgr4444 => 16, | ||||||
|  |             wl_shm::Format::Rgbx4444 => 16, | ||||||
|  |             wl_shm::Format::Bgrx4444 => 16, | ||||||
|  |             wl_shm::Format::Argb4444 => 16, | ||||||
|  |             wl_shm::Format::Abgr4444 => 16, | ||||||
|  |             wl_shm::Format::Rgba4444 => 16, | ||||||
|  |             wl_shm::Format::Bgra4444 => 16, | ||||||
|  |             wl_shm::Format::Xrgb1555 => 16, | ||||||
|  |             wl_shm::Format::Xbgr1555 => 16, | ||||||
|  |             wl_shm::Format::Rgbx5551 => 16, | ||||||
|  |             wl_shm::Format::Bgrx5551 => 16, | ||||||
|  |             wl_shm::Format::Argb1555 => 16, | ||||||
|  |             wl_shm::Format::Abgr1555 => 16, | ||||||
|  |             wl_shm::Format::Rgba5551 => 16, | ||||||
|  |             wl_shm::Format::Bgra5551 => 16, | ||||||
|  |             wl_shm::Format::Rgb565 => 16, | ||||||
|  |             wl_shm::Format::Bgr565 => 16, | ||||||
|  |             wl_shm::Format::Rgb888 => 24, | ||||||
|  |             wl_shm::Format::Bgr888 => 24, | ||||||
|  |             wl_shm::Format::Xbgr8888 => 32, | ||||||
|  |             wl_shm::Format::Rgbx8888 => 32, | ||||||
|  |             wl_shm::Format::Bgrx8888 => 32, | ||||||
|  |             wl_shm::Format::Abgr8888 => 32, | ||||||
|  |             wl_shm::Format::Rgba8888 => 32, | ||||||
|  |             wl_shm::Format::Bgra8888 => 32, | ||||||
|  |             wl_shm::Format::Xrgb2101010 => 32, | ||||||
|  |             wl_shm::Format::Xbgr2101010 => 32, | ||||||
|  |             wl_shm::Format::Rgbx1010102 => 32, | ||||||
|  |             wl_shm::Format::Bgrx1010102 => 32, | ||||||
|  |             wl_shm::Format::Argb2101010 => 32, | ||||||
|  |             wl_shm::Format::Abgr2101010 => 32, | ||||||
|  |             wl_shm::Format::Rgba1010102 => 32, | ||||||
|  |             wl_shm::Format::Bgra1010102 => 32, | ||||||
|  |             wl_shm::Format::R8 => 8, | ||||||
|  |             wl_shm::Format::R16 => 16, | ||||||
|  |             wl_shm::Format::Rg88 => 16, | ||||||
|  |             wl_shm::Format::Gr88 => 16, | ||||||
|  |             wl_shm::Format::Rg1616 => 32, | ||||||
|  |             wl_shm::Format::Gr1616 => 32, | ||||||
|  |             wl_shm::Format::Xrgb16161616f => 64, | ||||||
|  |             wl_shm::Format::Xbgr16161616f => 64, | ||||||
|  |             wl_shm::Format::Argb16161616f => 64, | ||||||
|  |             wl_shm::Format::Abgr16161616f => 64, | ||||||
|  |             wl_shm::Format::Xrgb8888A8 => 40, | ||||||
|  |             wl_shm::Format::Xbgr8888A8 => 40, | ||||||
|  |             wl_shm::Format::Rgbx8888A8 => 40, | ||||||
|  |             wl_shm::Format::Bgrx8888A8 => 40, | ||||||
|  |             wl_shm::Format::Rgb888A8 => 32, | ||||||
|  |             wl_shm::Format::Bgr888A8 => 32, | ||||||
|  |             wl_shm::Format::Rgb565A8 => 24, | ||||||
|  |             wl_shm::Format::Bgr565A8 => 24, | ||||||
|  |             wl_shm::Format::Axbxgxrx106106106106 => 64, | ||||||
|  |             wl_shm::Format::Xrgb16161616 => 64, | ||||||
|  |             wl_shm::Format::Xbgr16161616 => 64, | ||||||
|  |             wl_shm::Format::Argb16161616 => 64, | ||||||
|  |             wl_shm::Format::Abgr16161616 => 64, | ||||||
|  |             _ => 1, | ||||||
|  |         }) / 8 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn sample_r(&self, x: u32, y: u32) -> u8 { | ||||||
|  |         let offset = self.pixel_offset(x, y); | ||||||
|  |         let (shift, width) = match self.info.format { | ||||||
|  |             // (shift, width)
 | ||||||
|  |             wl_shm::Format::Argb8888 => (8 * 3, u8::BITS), | ||||||
|  |             wl_shm::Format::Xrgb8888 => (8 * 3, u8::BITS), | ||||||
|  |             wl_shm::Format::Xbgr8888 => (8 * 0, u8::BITS), | ||||||
|  |             wl_shm::Format::Abgr8888 => (8 * 0, u8::BITS), | ||||||
|  |             wl_shm::Format::Bgr888 => (8 * 2, u8::BITS), | ||||||
|  |             wl_shm::Format::Rgb888 => (0, u8::BITS), | ||||||
|  |             wl_shm::Format::Bgra8888 => (8 * 2, u8::BITS), | ||||||
|  |             wl_shm::Format::Bgrx8888 => (8 * 2, u8::BITS), | ||||||
|  |             wl_shm::Format::Rgba4444 => (0, u8::BITS / 2), | ||||||
|  |             wl_shm::Format::Rgbx4444 => (0, u8::BITS / 2), | ||||||
|  |             wl_shm::Format::Bgra4444 => (8, u8::BITS / 2), | ||||||
|  |             wl_shm::Format::Bgrx4444 => (8, u8::BITS / 2), | ||||||
|  |             wl_shm::Format::Argb4444 => (4, u8::BITS / 2), | ||||||
|  |             wl_shm::Format::Xrgb4444 => (4, u8::BITS / 2), | ||||||
|  |             wl_shm::Format::Rgba5551 => (0, 5), | ||||||
|  |             wl_shm::Format::Rgbx5551 => (0, 5), | ||||||
|  |             wl_shm::Format::Xrgb2101010 => (2, 10), | ||||||
|  |             wl_shm::Format::Argb2101010 => (2, 10), | ||||||
|  |             wl_shm::Format::Xrgb16161616 => (16, 16), | ||||||
|  |             wl_shm::Format::Argb16161616 => (16, 16), | ||||||
|  |             wl_shm::Format::Rgb332 => (0, 3), | ||||||
|  |             wl_shm::Format::Bgr233 => (5, 3), | ||||||
|  |             _ => unimplemented!(), | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         let offset = offset + (shift / 8) as usize; | ||||||
|  |         let shift = shift % 8; | ||||||
|  | 
 | ||||||
|  |         let acc = (self.bytes[offset] >> shift) & !(!1u8 << (width - 1).min(7)); | ||||||
|  |         let bits = (1..(width / 8)).fold(acc as u64, |acc, i| { | ||||||
|  |             // byte chunks:
 | ||||||
|  |             let mask = !(!1u8 << (width - 1 - i * 8).min(8)); | ||||||
|  | 
 | ||||||
|  |             let bits = self.bytes[offset + i as usize] & mask; | ||||||
|  |             (acc << 8) & bits as u64 | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         let bits = if width > 8 { | ||||||
|  |             bits >> (width - 8) | ||||||
|  |         } else if width < 8 { | ||||||
|  |             bits << (8 - width) | ||||||
|  |         } else { | ||||||
|  |             bits | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         bits as u8 | ||||||
|  |     } | ||||||
|  |     fn sample_g(&self, x: u32, y: u32) -> u8 { | ||||||
|  |         let offset = self.pixel_offset(x, y); | ||||||
|  |         let (shift, width) = match self.info.format { | ||||||
|  |             // (shift, width)
 | ||||||
|  |             wl_shm::Format::Argb8888 => (8 * 1, u8::BITS), | ||||||
|  |             wl_shm::Format::Xrgb8888 => (8 * 1, u8::BITS), | ||||||
|  |             wl_shm::Format::Xbgr8888 => (8 * 1, u8::BITS), | ||||||
|  |             wl_shm::Format::Abgr8888 => (8 * 1, u8::BITS), | ||||||
|  |             wl_shm::Format::Bgr888 => (8 * 1, u8::BITS), | ||||||
|  |             wl_shm::Format::Rgb888 => (8, u8::BITS), | ||||||
|  |             wl_shm::Format::Bgra8888 => (8 * 1, u8::BITS), | ||||||
|  |             wl_shm::Format::Bgrx8888 => (8 * 1, u8::BITS), | ||||||
|  |             wl_shm::Format::Rgba4444 => (4, u8::BITS / 2), | ||||||
|  |             wl_shm::Format::Rgbx4444 => (4, u8::BITS / 2), | ||||||
|  |             wl_shm::Format::Bgra4444 => (4, u8::BITS / 2), | ||||||
|  |             wl_shm::Format::Bgrx4444 => (4, u8::BITS / 2), | ||||||
|  |             wl_shm::Format::Argb4444 => (8, u8::BITS / 2), | ||||||
|  |             wl_shm::Format::Xrgb4444 => (8, u8::BITS / 2), | ||||||
|  |             wl_shm::Format::Rgba5551 => (5, 5), | ||||||
|  |             wl_shm::Format::Rgbx5551 => (5, 5), | ||||||
|  |             wl_shm::Format::Xrgb2101010 => (12, 10), | ||||||
|  |             wl_shm::Format::Argb2101010 => (12, 10), | ||||||
|  |             wl_shm::Format::Xrgb16161616 => (32, 16), | ||||||
|  |             wl_shm::Format::Argb16161616 => (32, 16), | ||||||
|  |             wl_shm::Format::Rgb332 => (3, 3), | ||||||
|  |             wl_shm::Format::Bgr233 => (2, 3), | ||||||
|  |             _ => unimplemented!(), | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         let offset = offset + (shift / 8) as usize; | ||||||
|  |         let shift = shift % 8; | ||||||
|  | 
 | ||||||
|  |         let acc = (self.bytes[offset] >> shift) & !(!1u8 << (width - 1).min(7)); | ||||||
|  |         let bits = (1..(width / 8)).fold(acc as u64, |acc, i| { | ||||||
|  |             // byte chunks:
 | ||||||
|  |             let mask = !(!1u8 << (width - 1 - i * 8).min(8)); | ||||||
|  | 
 | ||||||
|  |             let bits = self.bytes[offset + i as usize] & mask; | ||||||
|  |             (acc << 8) & bits as u64 | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         let bits = if width > 8 { | ||||||
|  |             bits >> (width - 8) | ||||||
|  |         } else if width < 8 { | ||||||
|  |             bits << (8 - width) | ||||||
|  |         } else { | ||||||
|  |             bits | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         bits as u8 | ||||||
|  |     } | ||||||
|  |     fn sample_b(&self, x: u32, y: u32) -> u8 { | ||||||
|  |         let offset = self.pixel_offset(x, y); | ||||||
|  |         let (shift, width) = match self.info.format { | ||||||
|  |             // (shift, width)
 | ||||||
|  |             wl_shm::Format::Argb8888 => (8 * 0, u8::BITS), | ||||||
|  |             wl_shm::Format::Xrgb8888 => (8 * 0, u8::BITS), | ||||||
|  |             wl_shm::Format::Xbgr8888 => (8 * 2, u8::BITS), | ||||||
|  |             wl_shm::Format::Abgr8888 => (8 * 2, u8::BITS), | ||||||
|  |             wl_shm::Format::Bgr888 => (8 * 0, u8::BITS), | ||||||
|  |             wl_shm::Format::Rgb888 => (8 * 2, u8::BITS), | ||||||
|  |             wl_shm::Format::Bgra8888 => (8 * 0, u8::BITS), | ||||||
|  |             wl_shm::Format::Bgrx8888 => (8 * 0, u8::BITS), | ||||||
|  |             wl_shm::Format::Rgba4444 => (8, u8::BITS / 2), | ||||||
|  |             wl_shm::Format::Rgbx4444 => (8, u8::BITS / 2), | ||||||
|  |             wl_shm::Format::Bgra4444 => (0, u8::BITS / 2), | ||||||
|  |             wl_shm::Format::Bgrx4444 => (0, u8::BITS / 2), | ||||||
|  |             wl_shm::Format::Argb4444 => (12, u8::BITS / 2), | ||||||
|  |             wl_shm::Format::Xrgb4444 => (12, u8::BITS / 2), | ||||||
|  |             wl_shm::Format::Rgba5551 => (10, 5), | ||||||
|  |             wl_shm::Format::Rgbx5551 => (10, 5), | ||||||
|  |             wl_shm::Format::Xrgb2101010 => (22, 10), | ||||||
|  |             wl_shm::Format::Argb2101010 => (22, 10), | ||||||
|  |             wl_shm::Format::Xrgb16161616 => (48, 16), | ||||||
|  |             wl_shm::Format::Argb16161616 => (48, 16), | ||||||
|  |             wl_shm::Format::Rgb332 => (6, 2), | ||||||
|  |             wl_shm::Format::Bgr233 => (0, 2), | ||||||
|  |             _ => unimplemented!(), | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         let offset = offset + (shift / 8) as usize; | ||||||
|  |         let shift = shift % 8; | ||||||
|  | 
 | ||||||
|  |         let acc = (self.bytes[offset] >> shift) & !(!1u8 << (width - 1).min(7)); | ||||||
|  |         let bits = (1..(width / 8)).fold(acc as u64, |acc, i| { | ||||||
|  |             // byte chunks:
 | ||||||
|  |             let mask = !(!1u8 << (width - 1 - i * 8).min(8)); | ||||||
|  | 
 | ||||||
|  |             let bits = self.bytes[offset + i as usize] & mask; | ||||||
|  |             (acc << 8) & bits as u64 | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         let bits = if width > 8 { | ||||||
|  |             bits >> (width - 8) | ||||||
|  |         } else if width < 8 { | ||||||
|  |             bits << (8 - width) | ||||||
|  |         } else { | ||||||
|  |             bits | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         bits as u8 | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl GenericImageView for BufferView<'_> { | ||||||
|  |     type Pixel = image::Rgb<u8>; | ||||||
|  | 
 | ||||||
|  |     fn dimensions(&self) -> (u32, u32) { | ||||||
|  |         (self.info.width, self.info.height) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn get_pixel(&self, x: u32, y: u32) -> Self::Pixel { | ||||||
|  |         match self.info.format { | ||||||
|  |             wl_shm::Format::Xbgr8888 => Self::Pixel::from( | ||||||
|  |                 <[u8; 3] as TryFrom<&[u8]>>::try_from(&self.bytes[self.pixel_offset(x, y)..][..3]) | ||||||
|  |                     .unwrap(), | ||||||
|  |             ), | ||||||
|  |             _ => Self::Pixel::from([ | ||||||
|  |                 self.sample_r(x, y), | ||||||
|  |                 self.sample_g(x, y), | ||||||
|  |                 self.sample_b(x, y), | ||||||
|  |             ]), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
		Loading…
	
		Reference in a new issue