notedeck

One damus client to rule them all
git clone git://jb55.com/notedeck
Log | Files | Refs | README | LICENSE

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 }