gif.rs (4853B)
1 use std::{ 2 sync::mpsc::TryRecvError, 3 time::{Instant, SystemTime}, 4 }; 5 6 use egui::TextureHandle; 7 use notedeck::{GifState, GifStateMap, TexturedImage}; 8 9 pub struct LatextTexture<'a> { 10 pub texture: &'a TextureHandle, 11 pub request_next_repaint: Option<SystemTime>, 12 } 13 14 /// This is necessary because other repaint calls can effectively steal our repaint request. 15 /// So we must keep on requesting to repaint at our desired time to ensure our repaint goes through. 16 /// See [`egui::Context::request_repaint_after`] 17 pub fn handle_repaint<'a>(ui: &egui::Ui, latest: LatextTexture<'a>) -> &'a TextureHandle { 18 if let Some(repaint) = latest.request_next_repaint { 19 if let Ok(dur) = repaint.duration_since(SystemTime::now()) { 20 ui.ctx().request_repaint_after(dur); 21 } 22 } 23 latest.texture 24 } 25 26 #[must_use = "caller should pass the return value to `gif::handle_repaint`"] 27 pub fn retrieve_latest_texture<'a>( 28 url: &str, 29 gifs: &'a mut GifStateMap, 30 cached_image: &'a mut TexturedImage, 31 ) -> LatextTexture<'a> { 32 match cached_image { 33 TexturedImage::Static(texture) => LatextTexture { 34 texture, 35 request_next_repaint: None, 36 }, 37 TexturedImage::Animated(animation) => { 38 if let Some(receiver) = &animation.receiver { 39 loop { 40 match receiver.try_recv() { 41 Ok(frame) => animation.other_frames.push(frame), 42 Err(TryRecvError::Empty) => { 43 break; 44 } 45 Err(TryRecvError::Disconnected) => { 46 animation.receiver = None; 47 break; 48 } 49 } 50 } 51 } 52 53 let now = Instant::now(); 54 let (texture, maybe_new_state, request_next_repaint) = match gifs.get(url) { 55 Some(prev_state) => { 56 let should_advance = 57 now - prev_state.last_frame_rendered >= prev_state.last_frame_duration; 58 59 if should_advance { 60 let maybe_new_index = if animation.receiver.is_some() 61 || prev_state.last_frame_index < animation.num_frames() - 1 62 { 63 prev_state.last_frame_index + 1 64 } else { 65 0 66 }; 67 68 match animation.get_frame(maybe_new_index) { 69 Some(frame) => { 70 let next_frame_time = SystemTime::now().checked_add(frame.delay); 71 ( 72 &frame.texture, 73 Some(GifState { 74 last_frame_rendered: now, 75 last_frame_duration: frame.delay, 76 next_frame_time, 77 last_frame_index: maybe_new_index, 78 }), 79 next_frame_time, 80 ) 81 } 82 None => { 83 let (tex, state) = 84 match animation.get_frame(prev_state.last_frame_index) { 85 Some(frame) => (&frame.texture, None), 86 None => (&animation.first_frame.texture, None), 87 }; 88 89 (tex, state, prev_state.next_frame_time) 90 } 91 } 92 } else { 93 let (tex, state) = match animation.get_frame(prev_state.last_frame_index) { 94 Some(frame) => (&frame.texture, None), 95 None => (&animation.first_frame.texture, None), 96 }; 97 (tex, state, prev_state.next_frame_time) 98 } 99 } 100 None => ( 101 &animation.first_frame.texture, 102 Some(GifState { 103 last_frame_rendered: now, 104 last_frame_duration: animation.first_frame.delay, 105 next_frame_time: None, 106 last_frame_index: 0, 107 }), 108 None, 109 ), 110 }; 111 112 if let Some(new_state) = maybe_new_state { 113 gifs.insert(url.to_owned(), new_state); 114 } 115 116 LatextTexture { 117 texture, 118 request_next_repaint, 119 } 120 } 121 } 122 }