diff options
author | Canop <cano.petrole@gmail.com> | 2022-01-06 22:47:39 +0100 |
---|---|---|
committer | Canop <cano.petrole@gmail.com> | 2022-01-06 22:47:39 +0100 |
commit | e89fb172d81fe99dd4d8a95b8784b2ed25a8ba37 (patch) | |
tree | 0a20fa8a00bf5f84a0be5fcb5bb6ee74b4d27465 /src | |
parent | be168b0de914ed04193ab734de0772615ae89493 (diff) |
determine when to draw/keep/erase kitty images
Benefits:
- fix background not erased
- fix flickering
- fix many redraws when computing sizes (or any background task)
Diffstat (limited to 'src')
-rw-r--r-- | src/app/app.rs | 20 | ||||
-rw-r--r-- | src/app/display_context.rs | 1 | ||||
-rw-r--r-- | src/image/image_view.rs | 61 | ||||
-rw-r--r-- | src/kitty/image_renderer.rs | 17 | ||||
-rw-r--r-- | src/kitty/image_set.rs | 37 | ||||
-rw-r--r-- | src/kitty/mod.rs | 91 | ||||
-rw-r--r-- | src/preview/preview.rs | 11 | ||||
-rw-r--r-- | src/preview/preview_state.rs | 3 |
8 files changed, 140 insertions, 101 deletions
diff --git a/src/app/app.rs b/src/app/app.rs index 921ff58..d7d1839 100644 --- a/src/app/app.rs +++ b/src/app/app.rs @@ -6,7 +6,9 @@ use { conf::Conf, display::{Areas, Screen, W}, errors::ProgramError, - file_sum, git, + file_sum, + git, + kitty, launchable::Launchable, path::closest_dir, skin::*, @@ -63,6 +65,9 @@ pub struct App { /// receiver to listen to the sequence channel rx_seqs: Receiver<Sequence>, + + /// counter incremented at every draw + drawing_count: usize, } impl App { @@ -99,6 +104,7 @@ impl App { shared_root: None, tx_seqs, rx_seqs, + drawing_count: 0, }) } @@ -194,15 +200,12 @@ impl App { app_state: &AppState, con: &AppContext, ) -> Result<(), ProgramError> { - // if some images are displayed by kitty, we'll erase them, - // but only after having displayed the new ones (if any) - // to prevent some flickerings - let previous_images = crate::kitty::take_current_images(); - + self.drawing_count += 1; for (idx, panel) in self.panels.as_mut_slice().iter_mut().enumerate() { let active = idx == self.active_panel_idx; let panel_skin = if active { &skin.focused } else { &skin.unfocused }; let disc = DisplayContext { + count: self.drawing_count, active, screen: self.screen, panel_skin, @@ -215,10 +218,7 @@ impl App { panel.display(w, &disc)?, ); } - - if let Some(previous_images) = previous_images { - previous_images.erase(w)?; - } + kitty::manager().lock().unwrap().erase_images_before(w, self.drawing_count)?; w.flush()?; Ok(()) } diff --git a/src/app/display_context.rs b/src/app/display_context.rs index 775bfe1..318c335 100644 --- a/src/app/display_context.rs +++ b/src/app/display_context.rs @@ -10,6 +10,7 @@ use { /// short lived wrapping of a few things which are needed for displaying /// panels pub struct DisplayContext<'c> { + pub count: usize, pub active: bool, pub screen: Screen, pub state_area: Area, diff --git a/src/image/image_view.rs b/src/image/image_view.rs index 9ec8708..f7cc687 100644 --- a/src/image/image_view.rs +++ b/src/image/image_view.rs @@ -1,9 +1,10 @@ use { super::double_line::DoubleLine, crate::{ - app::AppContext, + app::*, display::{Screen, W}, errors::ProgramError, + kitty::{self, KittyImageId}, skin::PanelSkin, }, crossterm::{ @@ -24,6 +25,19 @@ use { termimad::{fill_bg, Area}, }; +#[derive(Debug)] +struct DrawingInfo { + drawing_count: usize, + area: Area, +} + +impl DrawingInfo { + pub fn follows_in_place(&self, previous: &DrawingInfo) -> bool { + self.drawing_count == previous.drawing_count + 1 + && self.area == previous.area + } +} + /// an already resized image, with the dimensions it /// was computed for (which may be different from the /// dimensions we got) @@ -39,6 +53,8 @@ pub struct ImageView { path: PathBuf, source_img: DynamicImage, display_img: Option<CachedImage>, + last_drawing: Option<DrawingInfo>, + kitty_image_id: Option<KittyImageId>, } impl ImageView { @@ -52,6 +68,8 @@ impl ImageView { path: path.to_path_buf(), source_img, display_img: None, + last_drawing: None, + kitty_image_id: None, }) } pub fn is_png(&self) -> bool { @@ -63,17 +81,46 @@ impl ImageView { pub fn display( &mut self, w: &mut W, - _screen: Screen, - panel_skin: &PanelSkin, + disc: &DisplayContext, area: &Area, - con: &AppContext, ) -> Result<(), ProgramError> { - let styles = &panel_skin.styles; + + let styles = &disc.panel_skin.styles; let bg = styles.preview.get_bg() .or_else(|| styles.default.get_bg()) .unwrap_or(Color::AnsiValue(238)); - if crate::kitty::try_print_image(w, &self.source_img, area)? { + // we avoid drawing when we were just diplayed + // on the last drawing_count and the area is the same. + let drawing_info = DrawingInfo { + drawing_count: disc.count, + area: area.clone(), + }; + let must_draw = self.last_drawing + .as_ref() + .map_or(true, |previous| !drawing_info.follows_in_place(previous)); + if must_draw { + debug!("image_view must be cleared"); + } else { + debug!("no need to clear image_view"); + } + self.last_drawing = Some(drawing_info); + + let mut kitty_manager = kitty::manager().lock().unwrap(); + + if !must_draw { + if let Some(kitty_image_id) = self.kitty_image_id { + // we tell the manager the images must be kept, otherwise + // they would be erased at end of drawing, as obsolete + kitty_manager.keep(kitty_image_id, disc.count); + } + return Ok(()); + } + + self.kitty_image_id = kitty_manager + .try_print_image(w, &self.source_img, area, bg, disc.count)?; + + if self.kitty_image_id.is_some() { return Ok(()); } @@ -101,7 +148,7 @@ impl ImageView { let (width, height) = img.dimensions(); debug!("resized image dimensions: {},{}", width, height); debug_assert!(width <= area.width as u32); - let mut double_line = DoubleLine::new(width as usize, con.true_colors); + let mut double_line = DoubleLine::new(width as usize, disc.con.true_colors); let mut y = area.top; let img_top_offset = (area.height - (height / 2) as u16) / 2; for _ in 0..img_top_offset { diff --git a/src/kitty/image_renderer.rs b/src/kitty/image_renderer.rs index 58dab57..1402a65 100644 --- a/src/kitty/image_renderer.rs +++ b/src/kitty/image_renderer.rs @@ -12,6 +12,7 @@ use { crossterm::{ cursor, QueueableCommand, + style::Color, }, image::{ DynamicImage, @@ -23,7 +24,7 @@ use { io::{self, Write}, }, tempfile, - termimad::Area, + termimad::{fill_bg, Area}, }; /// How to send the image to kitty @@ -95,7 +96,7 @@ pub struct KittyImageRenderer { cell_width: u32, cell_height: u32, next_id: usize, - pub transmission_medium: TransmissionMedium, + transmission_medium: TransmissionMedium, } /// An image prepared for a precise area on screen @@ -217,14 +218,22 @@ impl KittyImageRenderer { self.next_id += 1; new_id } - /// Print the dynamicImage and return the KittyImageId - /// for later removal from screen + /// Clean the area, then print the dynamicImage and + /// return the KittyImageId for later removal from screen pub fn print( &mut self, w: &mut W, src: &DynamicImage, area: &Area, + bg: Color, ) -> Result<usize, ProgramError> { + + // clean the background below (and around) the image + for y in area.top..area.top + area.height { + w.queue(cursor::MoveTo(area.left, y))?; + fill_bg(w, area.width as usize, bg)?; + } + let img = KittyImage::new(src, area, self); debug!("transmission medium: {:?}", self.transmission_medium); match self.transmission_medium { diff --git a/src/kitty/image_set.rs b/src/kitty/image_set.rs deleted file mode 100644 index b818eaf..0000000 --- a/src/kitty/image_set.rs +++ /dev/null @@ -1,37 +0,0 @@ -use { - crate::{ - display::W, - errors::ProgramError, - }, - std::io::Write, -}; - -#[derive(Debug, Default)] -pub struct KittyImageSet { - ids: Vec<usize>, -} - -impl KittyImageSet { - pub fn erase( - self, - w: &mut W, - ) -> Result<(), ProgramError> { - for id in &self.ids { - debug!("erase kitty image {}", id); - write!(w, "\u{1b}_Ga=d,d=I,i={}\u{1b}\\", id)?; - } - Ok(()) - } - /// erase all kitty images, even the forgetted ones - /// - /// (this is currently unused) - pub fn erase_all( - w: &mut W, - ) -> Result<(), ProgramError> { - write!(w, "\u{1b}_Ga=d,d=A\u{1b}\\")?; - Ok(()) - } - pub fn push(&mut self, new_id: usize) { - self.ids.push(new_id); - } -} diff --git a/src/kitty/mod.rs b/src/kitty/mod.rs index adc9e45..907acbe 100644 --- a/src/kitty/mod.rs +++ b/src/kitty/mod.rs @@ -1,10 +1,8 @@ mod detect_support; mod image_renderer; -mod image_set; pub use { image_renderer::*, - image_set::*, }; use { @@ -12,51 +10,43 @@ use { display::W, errors::ProgramError, }, + crossterm::style::Color, image::DynamicImage, once_cell::sync::Lazy, - std::sync::Mutex, + std::{ + io::Write, + sync::Mutex, + }, termimad::Area, }; -/// Give the current images, so that they can be removed -/// (which should be done only after a new content has been -/// displayed) -pub fn take_current_images() -> Option<KittyImageSet> { - manager().lock().unwrap().take_current_images() -} -/// Try print the image in the specified area. -/// -/// Return Ok(true) if it went well, Ok(false) if -/// the terminal doesn't appear compatible with the Kitty -/// graphics protocol, or an error if somebody went wrong. -pub fn try_print_image( - w: &mut W, - src: &DynamicImage, - area: &Area, -) -> Result<bool, ProgramError> { - let mut manager = manager().lock().unwrap(); - manager.try_print_image(w, src, area) -} +pub type KittyImageId = usize; static MANAGER: Lazy<Mutex<KittyManager>> = Lazy::new(|| { let manager = KittyManager { - current_images: None, + rendered_images: Vec::new(), renderer: MaybeRenderer::Untested, }; Mutex::new(manager) }); -fn manager() -> &'static Mutex<KittyManager> { +pub fn manager() -> &'static Mutex<KittyManager> { &*MANAGER } #[derive(Debug)] -struct KittyManager { - current_images: Option<KittyImageSet>, +pub struct KittyManager { + rendered_images: Vec<RenderedImage>, renderer: MaybeRenderer, } #[derive(Debug)] +struct RenderedImage { + image_id: KittyImageId, + drawing_count: usize, +} + +#[derive(Debug)] enum MaybeRenderer { Untested, Disabled, @@ -66,9 +56,6 @@ enum MaybeRenderer { } impl KittyManager { - pub fn take_current_images(&mut self) -> Option<KittyImageSet> { - self.current_images.take() - } /// return the renderer if it's already checked and enabled, none if /// it's disabled or if it hasn't been tested yet pub fn renderer_if_tested(&mut self) -> Option<&mut KittyImageRenderer> { @@ -96,20 +83,52 @@ impl KittyManager { } } } + pub fn keep( + &mut self, + kept_id: KittyImageId, + drawing_count: usize, + ) { + for image in self.rendered_images.iter_mut() { + if image.image_id == kept_id { + image.drawing_count = drawing_count; + } + } + } pub fn try_print_image( &mut self, w: &mut W, src: &DynamicImage, area: &Area, - ) -> Result<bool, ProgramError> { + bg: Color, + drawing_count: usize, + ) -> Result<Option<KittyImageId>, ProgramError> { if let Some(renderer) = self.renderer() { - let new_id = renderer.print(w, src, area)?; - self.current_images - .get_or_insert_with(KittyImageSet::default) - .push(new_id); - Ok(true) + let new_id = renderer.print(w, src, area, bg)?; + self.rendered_images.push(RenderedImage { + image_id: new_id, + drawing_count, + }); + Ok(Some(new_id)) } else { - Ok(false) + Ok(None) + } + } + pub fn erase_images_before( + &mut self, + w: &mut W, + drawing_count: usize, + ) -> Result<(), ProgramError> { + let mut kept_images = Vec::new(); + for image in self.rendered_images.drain(..) { + if image.drawing_count >= drawing_count { + kept_images.push(image); + } else { + let id = image.image_id; + debug!("erase kitty image {}", id); + write!(w, "\u{1b}_Ga=d,d=I,i={}\u{1b}\\", id)?; + } } + self.rendered_images = kept_images; + Ok(()) } } diff --git a/src/preview/preview.rs b/src/preview/preview.rs index a80c4e3..d38d8a6 100644 --- a/src/preview/preview.rs +++ b/src/preview/preview.rs @@ -1,7 +1,7 @@ use { super::*, crate::{ - app::{AppContext, LineNumber}, + app::*, command::ScrollCommand, display::*, errors::ProgramError, @@ -225,13 +225,14 @@ impl Preview { pub fn display( &mut self, w: &mut W, - screen: Screen, - panel_skin: &PanelSkin, + disc: &DisplayContext, area: &Area, - con: &AppContext, ) -> Result<(), ProgramError> { + let panel_skin = &disc.panel_skin; + let screen = disc.screen; + let con = &disc.con; match self { - Self::Image(iv) => iv.display(w, screen, panel_skin, area, con), + Self::Image(iv) => iv.display(w, disc, area), Self::Syntactic(sv) => sv.display(w, screen, panel_skin, area, con), Self::ZeroLen(zlv) => zlv.display(w, screen, panel_skin, area), Self::Hex(hv) => hv.display(w, screen, panel_skin, area), diff --git a/src/preview/preview_state.rs b/src/preview/preview_state.rs index 34a5aae..99249b5 100644 --- a/src/preview/preview_state.rs +++ b/src/preview/preview_state.rs @@ -228,7 +228,6 @@ impl PanelState for PreviewState { w: &mut W, disc: &DisplayContext, ) -> Result<(), ProgramError> { - let con = &disc.con; let state_area = &disc.state_area; if state_area.height < 3 { warn!("area too small for preview"); @@ -264,7 +263,7 @@ impl PanelState for PreviewState { cw.fill(&styles.preview_title, &SPACE_FILLING)?; let preview = self.filtered_preview.as_mut().unwrap_or(&mut self.preview); preview.display_info(w, disc.screen, disc.panel_skin, &info_area)?; - if let Err(err) = preview.display(w, disc.screen, disc.panel_skin, &self.preview_area, con) { + if let Err(err) = preview.display(w, disc, &self.preview_area) { warn!("error while displaying file: {:?}", &err); if preview.get_mode().is_some() { // means it's not an error already |