use std::sync::Arc;
use std::time::Instant;
use std::cmp;
use xcb;
use xcbu;
use crate::{Display, error};
pub struct Screen {
display: Arc<Display>,
image: xcbu::image::shm::Image,
width: u32,
height: u32,
luminances: Vec<f32>,
luminance: u64,
cache: Vec<(u32, u32, u32, u32)>,
rated: Option<Instant>,
}
const PRECISION: f32 = 1_000_000.0;
impl Screen {
pub fn open(display: Arc<Display>, width: u32, height: u32) -> error::Result<Screen> {
let image = xcbu::image::shm::create(&display, 24, width as u16, height as u16)?;
let luminances = vec![0.0; (width * height) as usize];
Ok(Screen {
display, image,
width, height,
luminances, luminance: 0,
cache: Vec::new(),
rated: None,
})
}
pub fn resize(&mut self, width: u32, height: u32) -> error::Result<()> {
if self.image.actual_width() * self.image.actual_height() < (width * height) as u16 {
self.image = xcbu::image::shm::create(&self.display, 24, width as u16, height as u16)?;
}
else {
self.image.resize(width as u16, height as u16);
}
self.width = width;
self.height = height;
self.luminances.resize((width * height) as usize, 0.0);
self.luminance = 0;
for item in &mut self.luminances {
*item = 0.0;
}
self.refresh(0, 0, width, height)
}
pub fn flush(&mut self) -> error::Result<()> {
if self.rated.is_none() || self.cache.is_empty() {
return Ok(());
}
let (x, y, w, h) = self.cache.drain(..).fold((u32::max_value(), u32::max_value(), 0, 0),
|(xmin, ymin, xmax, ymax), (x, y, w, h)| {
(cmp::min(xmin, x), cmp::min(ymin, y), cmp::max(xmax, x + w), cmp::max(ymax, y + h))
});
self.rated = None;
self.refresh(x, y, w - x, h - y)
}
pub fn damage(&mut self, rect: xcb::Rectangle, threshold: u64) -> error::Result<bool> {
let x = rect.x() as u32;
let y = rect.y() as u32;
let w = u32::from(rect.width());
let h = u32::from(rect.height());
if self.rated.is_some() && u64::from(w * h) >= threshold {
self.cache.push((x, y, w, h));
Ok(false)
}
else if u64::from(w * h) >= threshold {
self.rated = Some(Instant::now());
self.refresh(x, y, w, h)?;
Ok(false)
}
else {
self.refresh(x, y, w, h)?;
Ok(true)
}
}
pub fn refresh(&mut self, x: u32, y: u32, width: u32, height: u32) -> error::Result<()> {
xcbu::image::shm::area(&self.display, self.display.root(), &mut self.image,
x as i16, y as i16, width as u16, height as u16, !0)?;
for xx in x .. width {
for yy in y .. height {
let rgb = {
let data = self.image.data();
let offset = (((xx - x) * 4) + ((yy - y) * width * 4)) as usize;
(data[offset], data[offset + 1], data[offset + 2])
};
self.put(xx, yy, rgb);
}
}
Ok(())
}
pub fn put(&mut self, x: u32, y: u32, (r, g, b): (u8, u8, u8)) -> f32 {
let r = f32::from(r) / 255.0;
let g = f32::from(g) / 255.0;
let b = f32::from(b) / 255.0;
let l = (r * 0.299) + (g * 0.587) + (b * 0.114);
let i = (x + (y * self.width)) as usize;
self.luminance -= (self.luminances[i].powi(2) * PRECISION) as u64;
self.luminance += (l.powi(2) * PRECISION) as u64;
self.luminances[i] = l;
l
}
pub fn luminance(&self) -> f32 {
((self.luminance as f32 / PRECISION) / (self.width * self.height) as f32).sqrt()
}
}