Skip to content

Commit

Permalink
webp support
Browse files Browse the repository at this point in the history
  • Loading branch information
mockersf committed Jun 8, 2024
1 parent 886d501 commit 2927b3c
Show file tree
Hide file tree
Showing 13 changed files with 449 additions and 330 deletions.
17 changes: 15 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "vleue_kinetoscope"
version = "0.1.1"
version = "0.2.0"
edition = "2021"
exclude = ["animated-gif.webp"]
authors = ["François Mockers <[email protected]>"]
Expand All @@ -13,11 +13,18 @@ license = "MIT OR Apache-2.0"
readme = "README.md"
categories = ["game-development"]

[features]
default = ["gif", "webp"]
gif = ["image/gif"]
webp = ["image/webp"]


[dependencies]
image = { version = "0.25", default-features = false }
thiserror = "1.0"
bevy = { version = "0.14.0-rc.2", default-features = false, features = [
"bevy_sprite",
] }
image = { version = "0.25", default-features = false, features = ["gif"] }

[dev-dependencies]
bevy = { version = "0.14.0-rc.2", default-features = false, features = [
Expand All @@ -31,3 +38,9 @@ bevy = { version = "0.14.0-rc.2", default-features = false, features = [
"ktx2",
"zstd",
] }

[patch.crates-io]
# For webp support - https://github.com/image-rs/image/pull/2228
image = { git = "https://github.com/image-rs/image" }
# For webp support - https://github.com/image-rs/image-webp/pull/76
image-webp = { git = "https://github.com/image-rs/image-webp" }
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
[![Bevy Tracking](https://img.shields.io/badge/Bevy%20tracking-main-lightblue)](https://github.com/bevyengine/bevy/blob/main/docs/plugins_guidelines.md#main-branch-tracking)
[![CI](https://github.com/vleue/vleue_kinetoscope/actions/workflows/ci.yml/badge.svg)](https://github.com/vleue/vleue_kinetoscope/actions/workflows/ci.yml)

Animated GIF player for Bevy.
Animated GIF and WebP player for Bevy.

![animated-gif](https://raw.githubusercontent.com/vleue/vleue_kinetoscope/main/animated-gif.webp)

Expand All @@ -19,27 +19,27 @@ Add the plugin to your app:

```rust
use bevy::prelude::*;
use vleue_kinetoscope::AnimatedGifPlugin;
use vleue_kinetoscope::AnimatedImagePlugin;

fn main() {
App::new()
// Usually included with `DefaultPlugins`
.add_plugins(AssetPlugin::default())
.add_plugins(AnimatedGifPlugin);
.add_plugins(AnimatedImagePlugin);
}
```

### Play an animated gif

Spawn an entity with the bundle `AnimatedGifImageBundle`
Spawn an entity with the bundle `AnimatedImageBundle`

```rust
use bevy::prelude::*;
use vleue_kinetoscope::*;

fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
commands.spawn(AnimatedGifImageBundle {
animated_gif: asset_server.load("Geneva_mechanism_6spoke_animation.gif"),
commands.spawn(AnimatedImageBundle {
animated_image: asset_server.load("Geneva_mechanism_6spoke_animation.gif"),
..default()
});
}
Expand Down
Binary file removed assets/Geneva_mechanism_6spoke_animation.gif
Binary file not shown.
Binary file added assets/cube.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/cube.webp
Binary file not shown.
142 changes: 142 additions & 0 deletions examples/animated-image.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
use bevy::prelude::*;

use vleue_kinetoscope::{AnimatedImageBundle, AnimatedImageController, AnimatedImagePlugin};

fn main() {
App::new()
.add_plugins(DefaultPlugins.set(ImagePlugin::default_nearest()))
.add_plugins(AnimatedImagePlugin)
.add_systems(Startup, setup)
.add_systems(Update, (log_updates, reset))
.run();
}

#[derive(Component, Clone, Copy, PartialEq, Eq, Debug)]
enum Image {
Gif,
Webp,
}

fn setup(mut commands: Commands, asset_server: Res<AssetServer>, window: Query<&Window>) {
commands.spawn(Camera2dBundle::default());

let window_width = window.single().width();

for (i, (kind, file)) in [(Image::Gif, "cube.gif"), (Image::Webp, "cube.webp")]
.into_iter()
.enumerate()
{
commands.spawn((
AnimatedImageBundle {
animated_image: asset_server.load(file),
transform: Transform::from_xyz(
-window_width * ((-1.0 as f32).powi(i as i32)) / 2.0,
-75.0,
0.0,
),
..default()
},
kind,
));

commands
.spawn(NodeBundle {
style: Style {
width: Val::Percent(50.0),
height: Val::Percent(100.0),
top: Val::Percent(10.0),
left: Val::Percent(50.0 * (i as f32)),
align_items: AlignItems::Start,
justify_content: JustifyContent::Center,
..default()
},
..default()
})
.with_children(|parent| {
parent.spawn((
TextBundle::from_sections(vec![
TextSection {
value: "play count: ".to_string(),
style: TextStyle {
font_size: 50.0,
..default()
},
},
TextSection {
value: "0".to_string(),
style: TextStyle {
font_size: 50.0,
..default()
},
},
TextSection {
value: "\ncurrent frame: ".to_string(),
style: TextStyle {
font_size: 30.0,
..default()
},
},
TextSection {
value: "0".to_string(),
style: TextStyle {
font_size: 30.0,
..default()
},
},
TextSection {
value: " / ".to_string(),
style: TextStyle {
font_size: 30.0,
..default()
},
},
TextSection {
value: "0".to_string(),
style: TextStyle {
font_size: 30.0,
..default()
},
},
TextSection {
value: format!("\n{:?}", kind),
style: TextStyle {
font_size: 20.0,
..default()
},
},
]),
kind,
));
});
}
}

fn log_updates(
mut texts: Query<(&mut Text, &Image)>,
playing_images: Query<(Ref<AnimatedImageController>, &Image)>,
) {
for (animated_image, image_kind) in &playing_images {
if animated_image.is_changed() {
for (mut text, text_kind) in &mut texts {
if image_kind != text_kind {
continue;
}
text.sections[1].value = format!("{}", animated_image.play_count());
text.sections[3].value = format!("{:>4}", animated_image.current_frame());
text.sections[5].value = format!("{}", animated_image.frame_count() as i32 - 1);
}
}
}
// let gif = gif.single();
}

fn reset(
keyboard_input: Res<ButtonInput<KeyCode>>,
mut playing_images: Query<&mut AnimatedImageController>,
) {
if keyboard_input.just_pressed(KeyCode::Space) {
for mut animated_image in &mut playing_images {
animated_image.reset();
}
}
}
95 changes: 0 additions & 95 deletions examples/gif-image.rs

This file was deleted.

60 changes: 60 additions & 0 deletions src/driver.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
use std::time::Duration;

use bevy::prelude::*;

use super::{AnimatedImage, AnimatedImageController};

pub(crate) fn image_driver(
images: Query<(Entity, &Handle<AnimatedImage>)>,
mut playing_images: Query<(
&Handle<AnimatedImage>,
&mut AnimatedImageController,
&mut Handle<Image>,
)>,
animated_images: Res<Assets<AnimatedImage>>,
time: Res<Time>,
) {
// don't rely on changed or added filter as the asset can be not yet loaded at the time the component is added
for (entity, new_animated_image) in images.iter() {
let Some(animated_image) = animated_images.get(new_animated_image) else {
continue;
};

if let Ok((_, mut controller, mut image)) = playing_images.get_mut(entity) {
if controller.current_frame == usize::MAX
|| !animated_image.frames.iter().any(|f| f.image == *image)
{
*controller = AnimatedImageController {
timer: Timer::new(
Duration::from_millis(animated_image.frames[0].delay.0 as u64),
TimerMode::Repeating,
),
current_frame: 0,
play_count: 0,
frame_count: animated_image.frames.len(),
};
*image = animated_image.frames[0].image.clone_weak();
}
}
}

for (animated_image, mut controller, mut image) in playing_images.iter_mut() {
if controller.timer.tick(time.delta()).just_finished() {
let Some(animated_image) = animated_images.get(animated_image) else {
continue;
};
let remaining = controller.timer.elapsed();
let new_index = (controller.current_frame + 1) % animated_image.frames.len();
controller.timer = Timer::new(
Duration::from_millis(animated_image.frames[new_index].delay.0 as u64),
TimerMode::Repeating,
);
controller.timer.set_elapsed(remaining);
*image = animated_image.frames[new_index].image.clone_weak();
controller.current_frame = new_index;
if new_index == 0 {
controller.play_count += 1;
}
}
}
}
Loading

0 comments on commit 2927b3c

Please sign in to comment.