256 lines
6.9 KiB
Rust
256 lines
6.9 KiB
Rust
use crate::core::{Color, Rectangle, Size};
|
|
use crate::graphics::compositor::{self, Information};
|
|
use crate::graphics::error::{self, Error};
|
|
use crate::graphics::{self, Viewport};
|
|
use crate::{Layer, Renderer, Settings};
|
|
|
|
use std::collections::VecDeque;
|
|
use std::num::NonZeroU32;
|
|
|
|
#[allow(missing_debug_implementations)]
|
|
pub struct Compositor {
|
|
context: softbuffer::Context<Box<dyn compositor::Window>>,
|
|
settings: Settings,
|
|
}
|
|
|
|
#[allow(missing_debug_implementations)]
|
|
pub struct Surface {
|
|
window: softbuffer::Surface<
|
|
Box<dyn compositor::Window>,
|
|
Box<dyn compositor::Window>,
|
|
>,
|
|
clip_mask: tiny_skia::Mask,
|
|
layer_stack: VecDeque<Vec<Layer>>,
|
|
background_color: Color,
|
|
max_age: u8,
|
|
}
|
|
|
|
impl crate::graphics::Compositor for Compositor {
|
|
type Renderer = Renderer;
|
|
type Surface = Surface;
|
|
|
|
async fn with_backend<W: compositor::Window>(
|
|
settings: graphics::Settings,
|
|
compatible_window: W,
|
|
backend: Option<&str>,
|
|
) -> Result<Self, Error> {
|
|
match backend {
|
|
None | Some("tiny-skia") | Some("tiny_skia") => {
|
|
Ok(new(settings.into(), compatible_window))
|
|
}
|
|
Some(backend) => Err(Error::GraphicsAdapterNotFound {
|
|
backend: "tiny-skia",
|
|
reason: error::Reason::DidNotMatch {
|
|
preferred_backend: backend.to_owned(),
|
|
},
|
|
}),
|
|
}
|
|
}
|
|
|
|
fn create_renderer(&self) -> Self::Renderer {
|
|
Renderer::new(
|
|
self.settings.default_font,
|
|
self.settings.default_text_size,
|
|
)
|
|
}
|
|
|
|
fn create_surface<W: compositor::Window + Clone>(
|
|
&mut self,
|
|
window: W,
|
|
width: u32,
|
|
height: u32,
|
|
) -> Self::Surface {
|
|
let window = softbuffer::Surface::new(
|
|
&self.context,
|
|
Box::new(window.clone()) as _,
|
|
)
|
|
.expect("Create softbuffer surface for window");
|
|
|
|
let mut surface = Surface {
|
|
window,
|
|
clip_mask: tiny_skia::Mask::new(width, height)
|
|
.expect("Create clip mask"),
|
|
layer_stack: VecDeque::new(),
|
|
background_color: Color::BLACK,
|
|
max_age: 0,
|
|
};
|
|
|
|
self.configure_surface(&mut surface, width, height);
|
|
|
|
surface
|
|
}
|
|
|
|
fn configure_surface(
|
|
&mut self,
|
|
surface: &mut Self::Surface,
|
|
width: u32,
|
|
height: u32,
|
|
) {
|
|
surface
|
|
.window
|
|
.resize(
|
|
NonZeroU32::new(width).expect("Non-zero width"),
|
|
NonZeroU32::new(height).expect("Non-zero height"),
|
|
)
|
|
.expect("Resize surface");
|
|
|
|
surface.clip_mask =
|
|
tiny_skia::Mask::new(width, height).expect("Create clip mask");
|
|
surface.layer_stack.clear();
|
|
}
|
|
|
|
fn fetch_information(&self) -> Information {
|
|
Information {
|
|
adapter: String::from("CPU"),
|
|
backend: String::from("tiny-skia"),
|
|
}
|
|
}
|
|
|
|
fn present<T: AsRef<str>>(
|
|
&mut self,
|
|
renderer: &mut Self::Renderer,
|
|
surface: &mut Self::Surface,
|
|
viewport: &Viewport,
|
|
background_color: Color,
|
|
overlay: &[T],
|
|
) -> Result<(), compositor::SurfaceError> {
|
|
present(renderer, surface, viewport, background_color, overlay)
|
|
}
|
|
|
|
fn screenshot<T: AsRef<str>>(
|
|
&mut self,
|
|
renderer: &mut Self::Renderer,
|
|
surface: &mut Self::Surface,
|
|
viewport: &Viewport,
|
|
background_color: Color,
|
|
overlay: &[T],
|
|
) -> Vec<u8> {
|
|
screenshot(renderer, surface, viewport, background_color, overlay)
|
|
}
|
|
}
|
|
|
|
pub fn new<W: compositor::Window>(
|
|
settings: Settings,
|
|
compatible_window: W,
|
|
) -> Compositor {
|
|
#[allow(unsafe_code)]
|
|
let context = softbuffer::Context::new(Box::new(compatible_window) as _)
|
|
.expect("Create softbuffer context");
|
|
|
|
Compositor { context, settings }
|
|
}
|
|
|
|
pub fn present<T: AsRef<str>>(
|
|
renderer: &mut Renderer,
|
|
surface: &mut Surface,
|
|
viewport: &Viewport,
|
|
background_color: Color,
|
|
overlay: &[T],
|
|
) -> Result<(), compositor::SurfaceError> {
|
|
let physical_size = viewport.physical_size();
|
|
|
|
let mut buffer = surface
|
|
.window
|
|
.buffer_mut()
|
|
.map_err(|_| compositor::SurfaceError::Lost)?;
|
|
|
|
let _last_layers = {
|
|
let age = buffer.age();
|
|
|
|
surface.max_age = surface.max_age.max(age);
|
|
surface.layer_stack.truncate(surface.max_age as usize);
|
|
|
|
if age > 0 {
|
|
surface.layer_stack.get(age as usize - 1)
|
|
} else {
|
|
None
|
|
}
|
|
};
|
|
|
|
// TODO
|
|
// let damage = last_layers
|
|
// .and_then(|last_layers| {
|
|
// (surface.background_color == background_color)
|
|
// .then(|| damage::layers(last_layers, renderer.layers()))
|
|
// })
|
|
// .unwrap_or_else(|| vec![Rectangle::with_size(viewport.logical_size())]);
|
|
|
|
let damage = vec![Rectangle::with_size(viewport.logical_size())];
|
|
|
|
if damage.is_empty() {
|
|
return Ok(());
|
|
}
|
|
|
|
surface
|
|
.layer_stack
|
|
.push_front(renderer.layers().cloned().collect());
|
|
surface.background_color = background_color;
|
|
|
|
// let damage = damage::group(damage, viewport.logical_size());
|
|
|
|
let mut pixels = tiny_skia::PixmapMut::from_bytes(
|
|
bytemuck::cast_slice_mut(&mut buffer),
|
|
physical_size.width,
|
|
physical_size.height,
|
|
)
|
|
.expect("Create pixel map");
|
|
|
|
renderer.draw(
|
|
&mut pixels,
|
|
&mut surface.clip_mask,
|
|
viewport,
|
|
&damage,
|
|
background_color,
|
|
overlay,
|
|
);
|
|
|
|
buffer.present().map_err(|_| compositor::SurfaceError::Lost)
|
|
}
|
|
|
|
pub fn screenshot<T: AsRef<str>>(
|
|
renderer: &mut Renderer,
|
|
surface: &mut Surface,
|
|
viewport: &Viewport,
|
|
background_color: Color,
|
|
overlay: &[T],
|
|
) -> Vec<u8> {
|
|
let size = viewport.physical_size();
|
|
|
|
let mut offscreen_buffer: Vec<u32> =
|
|
vec![0; size.width as usize * size.height as usize];
|
|
|
|
renderer.draw(
|
|
&mut tiny_skia::PixmapMut::from_bytes(
|
|
bytemuck::cast_slice_mut(&mut offscreen_buffer),
|
|
size.width,
|
|
size.height,
|
|
)
|
|
.expect("Create offscreen pixel map"),
|
|
&mut surface.clip_mask,
|
|
viewport,
|
|
&[Rectangle::with_size(Size::new(
|
|
size.width as f32,
|
|
size.height as f32,
|
|
))],
|
|
background_color,
|
|
overlay,
|
|
);
|
|
|
|
offscreen_buffer.iter().fold(
|
|
Vec::with_capacity(offscreen_buffer.len() * 4),
|
|
|mut acc, pixel| {
|
|
const A_MASK: u32 = 0xFF_00_00_00;
|
|
const R_MASK: u32 = 0x00_FF_00_00;
|
|
const G_MASK: u32 = 0x00_00_FF_00;
|
|
const B_MASK: u32 = 0x00_00_00_FF;
|
|
|
|
let a = ((A_MASK & pixel) >> 24) as u8;
|
|
let r = ((R_MASK & pixel) >> 16) as u8;
|
|
let g = ((G_MASK & pixel) >> 8) as u8;
|
|
let b = (B_MASK & pixel) as u8;
|
|
|
|
acc.extend([r, g, b, a]);
|
|
acc
|
|
},
|
|
)
|
|
}
|