use std::sync::{Arc, Mutex, MutexGuard, OnceLock};
use valo::{Context, ImageDesc};
use crate::{CompositionLayer, HardwareBuffer, Image, Picture, Placement};
static GPU: OnceLock<Mutex<Context>> = OnceLock::new();
pub fn solid_image(size: [u32; 2], color: [u8; 4]) -> Image {
let [width, height] = size;
let pixels: Vec<u8> = color
.iter()
.copied()
.cycle()
.take(width as usize * height as usize * 4)
.collect();
image_from_pixels(size, &pixels)
}
pub fn image_from_pixels(size: [u32; 2], pixels: &[u8]) -> Image {
let [width, height] = size;
assert_eq!(
pixels.len(),
width as usize * height as usize * 4,
"an image needs four bytes a pixel"
);
gpu().upload_image(
ImageDesc {
size,
premultiplied: false,
mips: false,
},
pixels,
)
}
pub fn drawn_pixels(picture: Picture, size: [u32; 2]) -> Vec<u8> {
let mut gpu = gpu();
let request = gpu.request_pixels(&Arc::new(picture), size, Some(valo::Color::TRANSPARENT));
gpu.device()
.poll(wgpu::PollType::wait_indefinitely())
.expect("the test GPU device answers");
pollster::block_on(request)
.expect("the picture reads back")
.into_bytes()
}
fn gpu() -> MutexGuard<'static, Context> {
GPU.get_or_init(|| Mutex::new(headless_context()))
.lock()
.expect("the test GPU device")
}
fn headless_context() -> Context {
let instance = wgpu::Instance::default();
let adapter =
pollster::block_on(instance.request_adapter(&wgpu::RequestAdapterOptions::default()))
.expect("a GPU adapter for tests");
let (device, queue) =
pollster::block_on(adapter.request_device(&wgpu::DeviceDescriptor::default()))
.expect("a GPU device for tests");
let mut context = Context::new(device, queue);
context.set_hide_missing_glyphs(true);
context
}
pub fn idle_layer() -> Arc<dyn CompositionLayer> {
Arc::new(IdleLayer)
}
struct IdleLayer;
impl CompositionLayer for IdleLayer {
fn present(
&self,
_buffer: HardwareBuffer,
_children: Vec<(Arc<dyn CompositionLayer>, Placement)>,
_released: Box<dyn FnOnce() + Send + Sync>,
) {
}
}