initial commit

This commit is contained in:
Janis 2024-11-26 20:35:38 +01:00
commit b647d95713
3 changed files with 959 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/target

18
Cargo.toml Normal file
View 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
View 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),
]),
}
}
}