use rustc_hash::FxHashMap;
use uuid::Uuid;
use vek::Vec4;
use crate::{Atom, VM};
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct StyleParams {
pub fill: Vec4<f32>,
pub border: Vec4<f32>,
pub radius_px: f32,
pub border_px: f32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct StyleId(Uuid);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
struct StyleKey {
fill: [u8; 4],
border: [u8; 4],
radius_q: u8,
border_q: u8,
}
struct StyleRecord {
id: StyleId,
tile_id: Uuid,
}
pub struct StyleRegistry {
styles: FxHashMap<StyleKey, StyleRecord>,
dirty: bool,
}
impl StyleRegistry {
pub fn new() -> Self {
Self {
styles: FxHashMap::default(),
dirty: false,
}
}
pub fn ensure_style(&mut self, vm: &mut VM, params: StyleParams) -> StyleId {
let key = to_key(params);
if let Some(rec) = self.styles.get(&key) {
return rec.id;
}
let tile_id = Uuid::new_v4();
let fill = to_u8(params.fill);
let border = to_u8(params.border);
let mut color_pixels = Vec::from(fill);
color_pixels.extend_from_slice(&border);
let widget_type = 1u8; let radius = params.radius_px.clamp(0.0, 255.0).round() as u8;
let border = params.border_px.clamp(0.0, 255.0).round() as u8;
let mat_tex0 = [widget_type, radius, 255, border];
let mat_tex1 = [widget_type, radius, 255, border]; let mut mat_pixels = Vec::from(mat_tex0);
mat_pixels.extend_from_slice(&mat_tex1);
vm.execute(Atom::AddTile {
id: tile_id,
width: 2,
height: 1,
frames: vec![color_pixels],
material_frames: Some(vec![mat_pixels]),
});
let id = StyleId(tile_id);
self.styles.insert(key, StyleRecord { id, tile_id });
vm.execute(Atom::BuildAtlas);
self.dirty = false;
id
}
pub fn tile_id(&self, id: StyleId) -> Option<Uuid> {
self.styles.values().find(|r| r.id == id).map(|r| r.tile_id)
}
pub fn build_if_dirty(&mut self, vm: &mut VM) {
if self.dirty {
vm.execute(Atom::BuildAtlas);
self.dirty = false;
}
}
}
fn to_u8(v: Vec4<f32>) -> [u8; 4] {
let clamp = |x: f32| (x.clamp(0.0, 1.0) * 255.0).round() as u8;
[clamp(v.x), clamp(v.y), clamp(v.z), clamp(v.w)]
}
fn to_key(p: StyleParams) -> StyleKey {
StyleKey {
fill: to_u8(p.fill),
border: to_u8(p.border),
radius_q: p.radius_px.clamp(0.0, 255.0).round() as u8,
border_q: p.border_px.clamp(0.0, 255.0).round() as u8,
}
}