use std::collections::HashMap;
use std::io::{self, Write};
use xei_core::media::ImageAsset;
use xei_core::App;
use crate::gpu_frame;
use crate::kitty_gfx;
use crate::term_caps::TerminalCaps;
struct Placed {
asset: ImageAsset,
at: Option<(u16, u16)>,
used: bool,
}
pub struct GfxRegistry {
items: HashMap<String, Placed>,
next_id: u32,
}
impl GfxRegistry {
pub fn new() -> Self {
Self {
items: HashMap::new(),
next_id: 300,
}
}
pub fn flush(
&mut self,
app: &mut App,
caps: &TerminalCaps,
editor_cursor: Option<(u16, u16)>,
) -> bool {
let enabled = kitty_gfx::available(app.gpu_acc && app.gpu_graphics, caps);
if !enabled && self.items.is_empty() {
return false;
}
let requests: Vec<(String, u16, u16, u16, u16)> = if enabled {
std::mem::take(&mut app.preview_gfx)
} else {
app.preview_gfx.clear();
Vec::new()
};
for p in self.items.values_mut() {
p.used = false;
}
let mut painted = false;
let use_sync = gpu_frame::should_sync(app.gpu_acc, caps);
let cell_px = app.cell_px_or_default();
let mut out = io::stdout();
for (path, x, y, w_cells, _rows) in requests {
let key = format!("{path}|{w_cells}");
if !self.items.contains_key(&key) {
let Ok(mut asset) = ImageAsset::load(std::path::Path::new(&path), cell_px)
else {
continue;
};
asset.width_cells = w_cells;
asset.kitty_id = self.next_id;
self.next_id = self.next_id.wrapping_add(1).max(300);
asset.rebuild_cache(cell_px);
self.items.insert(
key.clone(),
Placed {
asset,
at: None,
used: false,
},
);
}
let Some(item) = self.items.get_mut(&key) else {
continue;
};
item.used = true;
if item.at == Some((x, y)) {
continue; }
let a = &item.asset;
if kitty_gfx::place_rgba_rect_b64(
&mut out,
a.kitty_id,
a.cached_w,
a.cached_h,
Some(&a.cached_b64),
&a.cached_rgba,
x as u32,
y as u32,
editor_cursor,
use_sync,
)
.is_ok()
{
item.at = Some((x, y));
painted = true;
}
}
let stale: Vec<String> = self
.items
.iter()
.filter(|(_, p)| !p.used)
.map(|(k, _)| k.clone())
.collect();
for k in stale {
if let Some(p) = self.items.remove(&k) {
let _ = kitty_gfx::delete_image_flush(&mut out, p.asset.kitty_id);
painted = true;
}
}
let _ = out.flush();
painted
}
}