use image::DynamicImage;
use ratatui::layout::{Rect, Size};
use ratatui::Frame;
use ratatui_image::picker::{Picker, ProtocolType};
use ratatui_image::protocol::Protocol;
use ratatui_image::{Image, Resize};
pub struct Cover {
img: DynamicImage,
picker: Picker,
cached: Option<(Rect, Protocol)>,
}
impl Cover {
pub fn make_picker() -> Picker {
let mut picker = Picker::from_query_stdio().unwrap_or_else(|_| Picker::halfblocks());
if picker.protocol_type() == ProtocolType::Kitty
&& std::env::var("TERM_PROGRAM").is_ok_and(|t| t.contains("WarpTerminal"))
{
picker.set_protocol_type(ProtocolType::Iterm2);
}
if let Ok(want) = std::env::var("MYX_PROTOCOL") {
match want.to_ascii_lowercase().as_str() {
"kitty" => picker.set_protocol_type(ProtocolType::Kitty),
"iterm2" => picker.set_protocol_type(ProtocolType::Iterm2),
"sixel" => picker.set_protocol_type(ProtocolType::Sixel),
"halfblocks" => picker.set_protocol_type(ProtocolType::Halfblocks),
_ => {}
}
}
picker
}
pub fn load(path: &str, picker: Picker) -> Option<Self> {
let img = image::open(path).ok()?;
Some(Self::from_image(img, picker))
}
pub fn from_image(img: DynamicImage, picker: Picker) -> Self {
Self {
img,
picker,
cached: None,
}
}
pub fn placeholder(rgb: (u8, u8, u8), picker: Picker) -> Self {
let px = image::Rgb([rgb.0, rgb.1, rgb.2]);
let img = DynamicImage::ImageRgb8(image::RgbImage::from_pixel(2, 2, px));
Self::from_image(img, picker)
}
pub fn invalidate_cache(&mut self) {
self.cached = None;
}
pub fn render(&mut self, frame: &mut Frame, area: Rect) {
if area.width == 0 || area.height == 0 {
return;
}
let needs_encode = self
.cached
.as_ref()
.map(|(cached_area, _)| *cached_area != area)
.unwrap_or(true);
if needs_encode {
match self
.picker
.new_protocol(self.img.clone(), Size::new(area.width, area.height), Resize::Fit(None))
{
Ok(protocol) => self.cached = Some((area, protocol)),
Err(_) => return,
}
}
if let Some((_, protocol)) = &self.cached {
frame.render_widget(Image::new(protocol), area);
}
}
}