Skip to content

Commit

Permalink
feat: 🎸 init playback controls implementation (#31)
Browse files Browse the repository at this point in the history
* feat: 🎸 init playback controls implementation

* feat: 🎸 add speed & forward/reverse mode

* feat: 🎸 add Config struct

* fix: 🐛 autoplay when loaded only

* feat: 🎸 udl

* chore: 🤖 add load_animation_path

* chore: 🤖 add Bounce and ReverseBounce modes

* fix: 🐛 avoid copying the animation data
  • Loading branch information
theashraf authored Jan 12, 2024
1 parent 8d2e6ad commit 67ae22d
Show file tree
Hide file tree
Showing 15 changed files with 3,035 additions and 544 deletions.
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
{
"rust-analyzer.linkedProjects": [
"./dotlottie-rs/Cargo.toml",
"./dotlottie-ffi/Cargo.toml"
"./dotlottie-ffi/Cargo.toml",
"./demo-player/Cargo.toml"
],
"C_Cpp.errorSquiggles": "disabled"
}
284 changes: 27 additions & 257 deletions demo-player/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,170 +1,35 @@
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
// include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
use std::fs::read_to_string;
use std::{env, path, time::Instant};

use std::{
env,
fs::File,
io::Read,
path,
time::{Duration, Instant},
};

use dotlottie_player_core::DotLottiePlayer;
use dotlottie_player_core::{Config, DotLottiePlayer, Mode};
use minifb::{Key, Window, WindowOptions};

pub const WIDTH: usize = 1200;
pub const HEIGHT: usize = 1200;

// Todo
// - Accept a buffer containg json data
// - Accept an image buffer

// fn update_lottie(animation: *mut Tvg_Animation, canvas: *mut Tvg_Canvas, go_to_frame: &mut u32) {
// let mut total_frame: u32 = 0;
// let mut curr_frame: u32 = 0;

// unsafe { tvg_animation_get_total_frame(animation, &mut total_frame as *mut u32) };
// unsafe { tvg_animation_get_frame(animation, &mut curr_frame as *mut u32) };

// // let new_frame = total_frame * *go_to_frame;

// if *go_to_frame == curr_frame {
// return;
// }
// // println!("new frame {}", new_frame);
// println!("go_to_frame {}", *go_to_frame);

// unsafe { tvg_animation_set_frame(animation, *go_to_frame) };
// unsafe { tvg_canvas_update_paint(canvas, tvg_animation_get_picture(animation)) };

// //Draw the canvas
// unsafe { tvg_canvas_draw(canvas) };
// unsafe { tvg_canvas_sync(canvas) };
pub const WIDTH: usize = 500;
pub const HEIGHT: usize = 500;

// println!("curr frmae: {} ", curr_frame);
// println!("total frame: {} ", total_frame);

// if *go_to_frame >= total_frame {
// *go_to_frame = 0;
// }
// }

fn load_file(file_path: &str) -> (*const ::std::os::raw::c_char, String, u32) {
println!("Loading file: {}", file_path);

// Read a file in the local file system
let mut data_file = File::open(file_path).unwrap();

// Create an empty mutable string
let mut file_content = String::new();

// Copy contents of file to a mutable string
data_file.read_to_string(&mut file_content).unwrap();

return (
file_content.as_ptr() as *const i8,
file_content.clone(),
file_content.len() as u32,
);
}

// Tick will update the Lottie once a second has passed since last being called
struct Timer {
last_update: Instant,
}

impl Timer {
fn new() -> Self {
Timer {
Self {
last_update: Instant::now(),
}
}

fn tick(&mut self, animation: &mut DotLottiePlayer) {
let now = Instant::now();
let elapsed = now.duration_since(self.last_update);
// Parse this from the animation 'fr' value
let framerate = 30.0;
let frame_duration: u64 = (1.0 / framerate * 1000.0) as u64;

if elapsed >= Duration::from_millis(frame_duration) {
self.last_update = now; // Update the last update time
let next_frame = animation.request_frame();

let nextFrame = if animation.get_current_frame() >= animation.get_total_frame() {
0.0
} else {
animation.get_current_frame() + 1.0
};
println!("next_frame: {}", next_frame);
animation.set_frame(next_frame);
animation.render();

animation.frame(nextFrame);
}
self.last_update = Instant::now(); // Reset the timer
}
}

/*
Fill the buffer with animation data.
Returns the buffer filled with the first frame.
*/
// fn load_animation(buffer: &mut Vec<u32>, animation_data: &str, width: u32, height: u32) {
// let mut animation = std::ptr::null_mut();
// let mut canvas = std::ptr::null_mut();
// let mut frame_image = std::ptr::null_mut();
// let mut go_to_frame: u32 = 0;
// let mut duration: f32 = 0.0;
// let mimetype = CString::new("lottie").expect("Failed to create CString");

// println!("Loading up : {}", animation_data);

// unsafe {
// tvg_engine_init(Tvg_Engine_TVG_ENGINE_SW, 0);

// canvas = tvg_swcanvas_create();

// tvg_swcanvas_set_target(
// canvas,
// buffer.as_mut_ptr(),
// width,
// width,
// height,
// Tvg_Colorspace_TVG_COLORSPACE_ARGB8888,
// );
// }

// unsafe {
// animation = tvg_animation_new();
// frame_image = tvg_animation_get_picture(animation);

// let load_result = tvg_picture_load_data(
// frame_image,
// animation_data.as_ptr() as *const i8,
// animation_data.len() as u32,
// mimetype.as_ptr(),
// false,
// );

// if load_result != Tvg_Result_TVG_RESULT_SUCCESS {
// tvg_animation_del(animation);

// // DotLottieError::LoadContentError;
// } else {
// tvg_paint_scale(frame_image, 1.0);

// let mut total_frame: u32 = 0;
// tvg_animation_get_total_frame(animation, &mut total_frame as *mut u32);
// tvg_animation_get_duration(animation, &mut duration);
// tvg_animation_set_frame(animation, 0);
// tvg_canvas_push(canvas, frame_image);
// tvg_canvas_draw(canvas);
// tvg_canvas_sync(canvas);
// }
// }
// }

fn main() {
let mut buffer: *const u32;

let mut window = Window::new(
"Thorvg inside Rust - ESC to exit",
WIDTH,
Expand All @@ -175,125 +40,30 @@ fn main() {
panic!("{}", e);
});

let base_path = env::var("CARGO_MANIFEST_DIR").unwrap_or(
"/Users/sam/Projects/LottieFiles/Github/@rust/thorvg-rust-wrapper/core".to_string(),
);
let base_path = env::var("CARGO_MANIFEST_DIR").unwrap();

let mut path = path::PathBuf::from(base_path);
path.push("src/cartoon.json");

let result = load_file(path.to_str().expect("Animation file to exist"));

// load_animation(&mut buffer, result.1.as_str(), WIDTH as u32, HEIGHT as u32);

let mut lottie_player: DotLottiePlayer = DotLottiePlayer::new();
lottie_player.load_animation(result.1.as_str(), WIDTH as u32, HEIGHT as u32);

let buffer_slice = unsafe {
std::slice::from_raw_parts(lottie_player.get_buffer() as *const u32, WIDTH * HEIGHT * 4)
};
let mut lottie_player: DotLottiePlayer = DotLottiePlayer::new(Config {
mode: Mode::Reverse,
loop_animation: true,
speed: 2.5,
use_frame_interpolation: true,
autoplay: true,
});
lottie_player.load_animation_path(path.to_str().unwrap(), WIDTH as u32, HEIGHT as u32);

let mut timer = Timer::new();

while window.is_open() && !window.is_key_down(Key::Escape) {
timer.tick(&mut lottie_player);

window
.update_with_buffer(buffer_slice, WIDTH, HEIGHT)
.unwrap();
}
}

// fn main() {
// let mut buffer: Vec<u32> = vec![0; WIDTH * HEIGHT];

// let mut window = Window::new(
// "Thorvg inside Rust - ESC to exit",
// WIDTH,
// HEIGHT,
// WindowOptions::default(),
// )
// .unwrap_or_else(|e| {
// panic!("{}", e);
// });

// // window.limit_update_rate(Some(std::time::Duration::from_millis(30)));

// /*
// * Init Thorvg
// */
// unsafe { tvg_engine_init(Tvg_Engine_TVG_ENGINE_SW, 0) };

// let canvas = unsafe { tvg_swcanvas_create() };

// let raw_ptr = buffer.as_mut_ptr();

// unsafe {
// tvg_swcanvas_set_target(
// canvas,
// raw_ptr,
// WIDTH as u32,
// WIDTH as u32,
// HEIGHT as u32,
// Tvg_Colorspace_TVG_COLORSPACE_ARGB8888,
// );
// };

// /*
// Load a Lottie animation
// */
// let animation = unsafe { tvg_animation_new() };
// let mut duration = 0.0;
// let path = "/Users/sam/Projects/LottieFiles/Github/@rust/thorvg-rust-wrapper/src/cartoon.json";
let (buffer_ptr, buffer_len) = (lottie_player.buffer_ptr(), lottie_player.buffer_len());

// // Get a raw pointer to the CString's buffer
// let pict_lottie = unsafe { tvg_animation_get_picture(animation) };
let buffer =
unsafe { std::slice::from_raw_parts(buffer_ptr as *const u32, buffer_len as usize) };

// if pict_lottie.is_null() {
// println!("pict is null!");
// }

// let result = load_file(path);
// let mimetype = CString::new("lottie").expect("Failed to create CString");

// if (unsafe { tvg_picture_load_data(pict_lottie, result.0, result.1, mimetype.as_ptr(), false) }
// != Tvg_Result_TVG_RESULT_SUCCESS)
// {
// println!("Problem with loading an lottie file");

// unsafe { tvg_animation_del(animation) };
// } else {
// unsafe { tvg_paint_scale(pict_lottie, 1.0) };

// let mut total_frame: u32 = 0;
// unsafe { tvg_animation_get_total_frame(animation, &mut total_frame as *mut u32) };
// unsafe { tvg_animation_get_duration(animation, &mut duration) };

// unsafe { tvg_animation_set_frame(animation, 0) };

// unsafe { tvg_canvas_push(canvas, pict_lottie) };

// println!("Duration: {}", duration);

// println!("Total frames: {}", total_frame);
// }

// // unsafe { tvg_canvas_push(canvas, pict_lottie) };
// unsafe { tvg_canvas_draw(canvas) };
// unsafe { tvg_canvas_sync(canvas) };

// let mut go_to_frame: u32 = 0;
// let mut timer = Timer::new();
// while window.is_open() && !window.is_key_down(Key::Escape) {
// /*
// animation code
// */
// timer.tick(animation, canvas, &mut go_to_frame);

// if window.is_key_down(Key::Up) || window.is_key_down(Key::W) {}
// if window.is_key_down(Key::Down) || window.is_key_down(Key::S) {}
// if window.is_key_down(Key::Left) || window.is_key_down(Key::A) {}
// if window.is_key_down(Key::Right) || window.is_key_down(Key::D) {}

// window.update_with_buffer(&buffer, WIDTH, HEIGHT).unwrap();
// }
// }
window.update_with_buffer(buffer, WIDTH, HEIGHT).unwrap();
}
}
18 changes: 3 additions & 15 deletions dotlottie-ffi/emscripten_bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,7 @@ using namespace dotlottie_player;

val getBuffer(DotLottiePlayer &player)
{
auto buffer_ptr = player.get_buffer();
auto buffer_size = player.get_buffer_size();
return val(typed_memory_view(buffer_size, reinterpret_cast<uint8_t *>(buffer_ptr)));
auto buffer_ptr = player.buffer_ptr();
auto buffer_len = player.buffer_len();
return val(typed_memory_view(buffer_len, reinterpret_cast<uint8_t *>(buffer_ptr)));
}

EMSCRIPTEN_BINDINGS(DotLottiePlayer)
{
class_<DotLottiePlayer>("DotLottiePlayer")
.constructor(&DotLottiePlayer::init)
.function("frame", &DotLottiePlayer::frame)
.function("getCurrentFrame", &DotLottiePlayer::get_current_frame)
.function("getDuration", &DotLottiePlayer::get_duration)
.function("getTotalFrame", &DotLottiePlayer::get_total_frame)
.function("loadAnimation", &DotLottiePlayer::load_animation)
.function("getBuffer", &getBuffer);
}
Loading

0 comments on commit 67ae22d

Please sign in to comment.