use std::{ sync::mpsc::TryRecvError, time::{Instant, SystemTime}, }; use egui::TextureHandle; use notedeck::{GifState, GifStateMap, TexturedImage}; pub struct LatextTexture<'a> { pub texture: &'a TextureHandle, pub request_next_repaint: Option, } /// This is necessary because other repaint calls can effectively steal our repaint request. /// So we must keep on requesting to repaint at our desired time to ensure our repaint goes through. /// See [`egui::Context::request_repaint_after`] pub fn handle_repaint<'a>(ui: &egui::Ui, latest: LatextTexture<'a>) -> &'a TextureHandle { if let Some(repaint) = latest.request_next_repaint { if let Ok(dur) = repaint.duration_since(SystemTime::now()) { ui.ctx().request_repaint_after(dur); } } latest.texture } #[must_use = "caller should pass the return value to `gif::handle_repaint`"] pub fn retrieve_latest_texture<'a>( url: &str, gifs: &'a mut GifStateMap, cached_image: &'a mut TexturedImage, ) -> LatextTexture<'a> { match cached_image { TexturedImage::Static(texture) => LatextTexture { texture, request_next_repaint: None, }, TexturedImage::Animated(animation) => { if let Some(receiver) = &animation.receiver { loop { match receiver.try_recv() { Ok(frame) => animation.other_frames.push(frame), Err(TryRecvError::Empty) => { break; } Err(TryRecvError::Disconnected) => { animation.receiver = None; break; } } } } let now = Instant::now(); let (texture, maybe_new_state, request_next_repaint) = match gifs.get(url) { Some(prev_state) => { let should_advance = now - prev_state.last_frame_rendered >= prev_state.last_frame_duration; if should_advance { let maybe_new_index = if animation.receiver.is_some() || prev_state.last_frame_index < animation.num_frames() - 1 { prev_state.last_frame_index + 1 } else { 0 }; match animation.get_frame(maybe_new_index) { Some(frame) => { let next_frame_time = SystemTime::now().checked_add(frame.delay); ( &frame.texture, Some(GifState { last_frame_rendered: now, last_frame_duration: frame.delay, next_frame_time, last_frame_index: maybe_new_index, }), next_frame_time, ) } None => { let (tex, state) = match animation.get_frame(prev_state.last_frame_index) { Some(frame) => (&frame.texture, None), None => (&animation.first_frame.texture, None), }; (tex, state, prev_state.next_frame_time) } } } else { let (tex, state) = match animation.get_frame(prev_state.last_frame_index) { Some(frame) => (&frame.texture, None), None => (&animation.first_frame.texture, None), }; (tex, state, prev_state.next_frame_time) } } None => ( &animation.first_frame.texture, Some(GifState { last_frame_rendered: now, last_frame_duration: animation.first_frame.delay, next_frame_time: None, last_frame_index: 0, }), None, ), }; if let Some(new_state) = maybe_new_state { gifs.insert(url.to_owned(), new_state); } LatextTexture { texture, request_next_repaint, } } } }