gpui: Add support for animated images (#13809)

This PR adds support for animated images. The image requires a id for it
to actually animate across frames.

Currently it only has support for `GIF`, I tried adding decoding a
animated `WebP` into frames but it seems to error. This issue in the
image crate seems to document this
https://github.com/image-rs/image/issues/2263.

Not sure if this is the best way or the desired way for animated images
to work in GPUI but I would really like support for animated images.
Open to feedback.

Example Video:


https://github.com/zed-industries/zed/assets/76515905/011f790f-d070-499b-96c9-bbff141fb002



Closes https://github.com/zed-industries/zed/issues/9993

Release Notes:

- N/A

---------

Co-authored-by: Antonio Scandurra <me@as-cii.com>
Co-authored-by: Nathan <nathan@zed.dev>
This commit is contained in:
Matin Aniss 2024-07-27 22:05:37 +10:00 committed by GitHub
parent c0df1e1846
commit 4bd935b409
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 212 additions and 51 deletions

View file

@ -1158,6 +1158,23 @@ impl<'a> WindowContext<'a> {
RefCell::borrow_mut(&self.window.next_frame_callbacks).push(Box::new(callback));
}
/// Schedule a frame to be drawn on the next animation frame.
///
/// This is useful for elements that need to animate continuously, such as a video player or an animated GIF.
/// It will cause the window to redraw on the next frame, even if no other changes have occurred.
///
/// If called from within a view, it will notify that view on the next frame. Otherwise, it will refresh the entire window.
pub fn request_animation_frame(&mut self) {
let parent_id = self.parent_view_id();
self.on_next_frame(move |cx| {
if let Some(parent_id) = parent_id {
cx.notify(parent_id)
} else {
cx.refresh()
}
});
}
/// Spawn the future returned by the given closure on the application thread pool.
/// The closure is provided a handle to the current window and an `AsyncWindowContext` for
/// use within your future.
@ -2602,6 +2619,7 @@ impl<'a> WindowContext<'a> {
bounds: Bounds<Pixels>,
corner_radii: Corners<Pixels>,
data: Arc<ImageData>,
frame_index: usize,
grayscale: bool,
) -> Result<()> {
debug_assert_eq!(
@ -2612,13 +2630,19 @@ impl<'a> WindowContext<'a> {
let scale_factor = self.scale_factor();
let bounds = bounds.scale(scale_factor);
let params = RenderImageParams { image_id: data.id };
let params = RenderImageParams {
image_id: data.id,
frame_index,
};
let tile = self
.window
.sprite_atlas
.get_or_insert_with(&params.clone().into(), &mut || {
Ok(Some((data.size(), Cow::Borrowed(data.as_bytes()))))
Ok(Some((
data.size(frame_index),
Cow::Borrowed(data.as_bytes(frame_index)),
)))
})?
.expect("Callback above only returns Some");
let content_mask = self.content_mask().scale(scale_factor);