use crate::core::{Color, Rectangle, Size}; use crate::graphics::compositor::{self, Information}; use crate::graphics::damage; use crate::graphics::{Error, Viewport}; use crate::{Backend, Primitive, Renderer, Settings}; use raw_window_handle::{HasDisplayHandle, HasWindowHandle}; use std::collections::VecDeque; use std::marker::PhantomData; use std::num::NonZeroU32; pub struct Compositor { context: Option>, settings: Settings, _theme: PhantomData, } pub struct Surface { window: softbuffer::Surface, clip_mask: tiny_skia::Mask, // Primitives of existing buffers, by decreasing age primitives: VecDeque>, background_color: Color, } // XXX avoid clone bound? impl crate::graphics::Compositor for Compositor { type Settings = Settings; type Renderer = Renderer; type Surface = Surface; fn new( settings: Self::Settings, compatible_window: Option, ) -> Result { Ok(new(settings, compatible_window)) } fn create_renderer(&self) -> Self::Renderer { Renderer::new( Backend::new(), self.settings.default_font, self.settings.default_text_size, ) } fn create_surface( &mut self, window: W, width: u32, height: u32, ) -> Surface { let window = if let Some(context) = self.context.as_ref() { softbuffer::Surface::new(context, window) .expect("Create softbuffer surface for window") } else { let context = softbuffer::Context::new(window.clone()) .expect("Create softbuffer context for window"); softbuffer::Surface::new(&context, window) .expect("Create softbuffer surface for window") }; Surface { window, clip_mask: tiny_skia::Mask::new(width, height) .expect("Create clip mask"), primitives: VecDeque::new(), background_color: Color::BLACK, } } fn configure_surface( &mut self, surface: &mut Surface, width: u32, height: u32, ) { surface.clip_mask = tiny_skia::Mask::new(width, height).expect("Create clip mask"); surface.primitives.clear(); } fn fetch_information(&self) -> Information { Information { adapter: String::from("CPU"), backend: String::from("tiny-skia"), } } fn present>( &mut self, renderer: &mut Self::Renderer, surface: &mut Surface, viewport: &Viewport, background_color: Color, overlay: &[T], ) -> Result<(), compositor::SurfaceError> { renderer.with_primitives(|backend, primitives| { present( backend, surface, primitives, viewport, background_color, overlay, ) }) } fn screenshot>( &mut self, renderer: &mut Self::Renderer, surface: &mut Self::Surface, viewport: &Viewport, background_color: Color, overlay: &[T], ) -> Vec { renderer.with_primitives(|backend, primitives| { screenshot( surface, backend, primitives, viewport, background_color, overlay, ) }) } } pub fn new( settings: Settings, compatible_window: Option, ) -> Compositor { #[allow(unsafe_code)] let context = compatible_window.and_then(|w| softbuffer::Context::new(w).ok()); Compositor { context, settings, _theme: PhantomData, } } pub fn present>( backend: &mut Backend, surface: &mut Surface, primitives: &[Primitive], viewport: &Viewport, background_color: Color, overlay: &[T], ) -> Result<(), compositor::SurfaceError> { let physical_size = viewport.physical_size(); let scale_factor = viewport.scale_factor() as f32; surface .window .resize( NonZeroU32::new(physical_size.width).unwrap(), NonZeroU32::new(physical_size.height).unwrap(), ) .unwrap(); // TODO Add variants to `SurfaceError`? let mut buffer = surface .window .buffer_mut() .map_err(|_| compositor::SurfaceError::Lost)?; let age = buffer.age(); // Forget primatives for back buffers older than `age` // Or if this is a new buffer, keep at most two. let max = if age == 0 { 2 } else { age }; while surface.primitives.len() as u8 > max { let _ = surface.primitives.pop_front(); } let last_primitives = if surface.primitives.len() as u8 == age { surface.primitives.pop_front() } else { None }; let damage = last_primitives .and_then(|last_primitives| { (surface.background_color == background_color) .then(|| damage::list(&last_primitives, primitives)) }) .unwrap_or_else(|| vec![Rectangle::with_size(viewport.logical_size())]); surface.primitives.push_back(primitives.to_vec()); surface.background_color = background_color; if !damage.is_empty() { let damage = damage::group(damage, scale_factor, physical_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"); backend.draw( &mut pixels, &mut surface.clip_mask, primitives, viewport, &damage, background_color, overlay, ); } buffer.present().map_err(|_| compositor::SurfaceError::Lost) } pub fn screenshot>( surface: &mut Surface, backend: &mut Backend, primitives: &[Primitive], viewport: &Viewport, background_color: Color, overlay: &[T], ) -> Vec { let size = viewport.physical_size(); let mut offscreen_buffer: Vec = vec![0; size.width as usize * size.height as usize]; backend.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, primitives, 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 }, ) }