123 lines
4.7 KiB
Rust
123 lines
4.7 KiB
Rust
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<SystemTime>,
|
|
}
|
|
|
|
/// 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,
|
|
}
|
|
}
|
|
}
|
|
}
|