From 0dc51f171cf492a2781c8d0266560f6908717a5a Mon Sep 17 00:00:00 2001 From: Abdelrahman Ashraf Date: Tue, 16 Jan 2024 10:47:26 +0700 Subject: [PATCH 1/5] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20segments?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- demo-player/src/main.rs | 1 + dotlottie-ffi/src/dotlottie_player.udl | 1 + dotlottie-rs/src/dotlottie_player.rs | 68 +++++++++++++++++++------- 3 files changed, 53 insertions(+), 17 deletions(-) diff --git a/demo-player/src/main.rs b/demo-player/src/main.rs index 2eedf11e..6b637727 100644 --- a/demo-player/src/main.rs +++ b/demo-player/src/main.rs @@ -51,6 +51,7 @@ fn main() { speed: 2.5, use_frame_interpolation: true, autoplay: true, + segments: Some(vec![10.0, 45.0]), }); lottie_player.load_animation_path(path.to_str().unwrap(), WIDTH as u32, HEIGHT as u32); diff --git a/dotlottie-ffi/src/dotlottie_player.udl b/dotlottie-ffi/src/dotlottie_player.udl index 330d2048..6928d7e5 100644 --- a/dotlottie-ffi/src/dotlottie_player.udl +++ b/dotlottie-ffi/src/dotlottie_player.udl @@ -14,6 +14,7 @@ dictionary Config { Mode mode; f32 speed; boolean use_frame_interpolation; + sequence? segments; }; interface DotLottiePlayer { diff --git a/dotlottie-rs/src/dotlottie_player.rs b/dotlottie-rs/src/dotlottie_player.rs index 15ef170d..cd31101e 100644 --- a/dotlottie-rs/src/dotlottie_player.rs +++ b/dotlottie-rs/src/dotlottie_player.rs @@ -17,13 +17,14 @@ pub enum Mode { ReverseBounce, } -#[derive(Clone, Copy)] +#[derive(Clone)] pub struct Config { pub mode: Mode, pub loop_animation: bool, pub speed: f32, pub use_frame_interpolation: bool, pub autoplay: bool, + pub segments: Option>, } struct DotLottieRuntime { @@ -47,6 +48,20 @@ impl DotLottieRuntime { } } + fn start_frame(&self) -> f32 { + match &self.config.segments { + Some(segments) => segments[0], + None => 0.0, + } + } + + fn end_frame(&self) -> f32 { + match &self.config.segments { + Some(segments) => segments[1], + None => self.total_frames(), + } + } + pub fn is_loaded(&self) -> bool { self.is_loaded } @@ -95,12 +110,16 @@ impl DotLottieRuntime { pub fn stop(&mut self) -> bool { if self.is_loaded { self.playback_state = PlaybackState::Stopped; + + let start_frame = self.start_frame(); + let end_frame = self.end_frame(); + match self.config.mode { Mode::Forward => { - self.set_frame(0_f32); + self.set_frame(start_frame); } Mode::Reverse => { - self.set_frame(self.total_frames()); + self.set_frame(end_frame); } _ => {} } @@ -118,10 +137,23 @@ impl DotLottieRuntime { let elapsed_time = self.start_time.elapsed().as_secs_f32(); - let duration = self.duration() / self.config.speed; - let total_frames = self.total_frames() - 1.0; + // the animation total frames + let total_frames = self.total_frames(); + // the animation duration in seconds + let duration = self.duration(); + + // the animation start & end frames (considering the segments) + let start_frame = self.start_frame(); + let end_frame = self.end_frame(); + + // the effective total frames (considering the segments) + let effective_total_frames = end_frame - start_frame; - let raw_next_frame = (elapsed_time / duration) * total_frames; + // the effective duration in milliseconds (considering the segments & speed) + let effective_duration = + (1000.0 * duration * effective_total_frames / total_frames) / self.config.speed; + + let raw_next_frame = elapsed_time / effective_duration * effective_total_frames; let next_frame = if self.config.use_frame_interpolation { raw_next_frame @@ -131,19 +163,19 @@ impl DotLottieRuntime { let next_frame = match self.config.mode { Mode::Forward => next_frame, - Mode::Reverse => total_frames - next_frame, + Mode::Reverse => effective_total_frames - next_frame, _ => next_frame, }; let next_frame = match self.config.mode { Mode::Forward => { - if next_frame >= total_frames { + if next_frame >= effective_total_frames { if self.config.loop_animation { self.loop_count += 1; self.start_time = Instant::now(); 0.0 } else { - total_frames + effective_total_frames } } else { next_frame @@ -154,7 +186,7 @@ impl DotLottieRuntime { if self.config.loop_animation { self.loop_count += 1; self.start_time = Instant::now(); - total_frames + effective_total_frames } else { 0.0 } @@ -193,7 +225,7 @@ impl DotLottieRuntime { } pub fn set_speed(&mut self, speed: f32) { - self.config.speed = if speed < 0.0 { 0.0 } else { speed }; + self.config.speed = if speed < 0.0 { 1.0 } else { speed }; } pub fn speed(&self) -> f32 { @@ -220,14 +252,15 @@ impl DotLottieRuntime { self.is_loaded = loaded; - let total_frames = self.total_frames(); + let start_frame = self.start_frame(); + let end_frame = self.end_frame(); match self.config.mode { Mode::Forward => { - self.set_frame(0_f32); + self.set_frame(start_frame); } Mode::Reverse => { - self.set_frame(total_frames); + self.set_frame(end_frame); } _ => {} } @@ -247,14 +280,15 @@ impl DotLottieRuntime { self.is_loaded = loaded; - let total_frames = self.total_frames(); + let start_frame = self.start_frame(); + let end_frame = self.end_frame(); match self.config.mode { Mode::Forward => { - self.set_frame(0_f32); + self.set_frame(start_frame); } Mode::Reverse => { - self.set_frame(total_frames); + self.set_frame(end_frame); } _ => {} } From f1dc4d84a938d874f61fabd5686d994da8beafb6 Mon Sep 17 00:00:00 2001 From: Abdelrahman Ashraf Date: Wed, 17 Jan 2024 12:21:45 +0700 Subject: [PATCH 2/5] =?UTF-8?q?fix:=20=F0=9F=90=9B=20wasm=20bindings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Makefile | 2 +- demo-player/src/main.rs | 3 +- deps/modules/emsdk | 2 +- dotlottie-ffi/emscripten_bindings.cpp | 7 +++- dotlottie-ffi/src/dotlottie_player.udl | 2 +- dotlottie-rs/src/dotlottie_player.rs | 48 ++++++++++++++------------ web-example.html | 11 ++++++ 7 files changed, 46 insertions(+), 29 deletions(-) diff --git a/Makefile b/Makefile index 1d76d3cf..499ba5c5 100644 --- a/Makefile +++ b/Makefile @@ -761,7 +761,7 @@ $(eval $(call NEW_LOCAL_ARCH_CMAKE_BUILD,ZLIB,$(ZLIB),$(ZLIB_LIB))) $(THORVG_LOCAL_ARCH_BUILD_DIR)/$(NINJA_BUILD_FILE): export PKG_CONFIG_PATH := $(PWD)/$(LOCAL_ARCH_LIB_DIR)/pkgconfig:$(PWD)/$(LOCAL_ARCH_LIB64_DIR) $(THORVG_LOCAL_ARCH_BUILD_DIR)/$(NINJA_BUILD_FILE): THORVG_DEP_SOURCE_DIR := $(DEPS_MODULES_DIR)/$(THORVG) $(THORVG_LOCAL_ARCH_BUILD_DIR)/$(NINJA_BUILD_FILE): THORVG_DEP_BUILD_DIR := $(THORVG_LOCAL_ARCH_BUILD_DIR) -$(THORVG_LOCAL_ARCH_BUILD_DIR)/$(NINJA_BUILD_FILE): LOG := true +$(THORVG_LOCAL_ARCH_BUILD_DIR)/$(NINJA_BUILD_FILE): LOG := false $(THORVG_LOCAL_ARCH_BUILD_DIR)/$(NINJA_BUILD_FILE): STATIC := false $(THORVG_LOCAL_ARCH_BUILD_DIR)/$(NINJA_BUILD_FILE): SAVERS := all $(THORVG_LOCAL_ARCH_BUILD_DIR)/$(NINJA_BUILD_FILE): $(LOCAL_ARCH_LIB_DIR)/$(LIBJPEG_TURBO_LIB) diff --git a/demo-player/src/main.rs b/demo-player/src/main.rs index 6b637727..659b9919 100644 --- a/demo-player/src/main.rs +++ b/demo-player/src/main.rs @@ -1,4 +1,3 @@ -use std::fs::read_to_string; use std::{env, path, time::Instant}; use dotlottie_player_core::{Config, DotLottiePlayer, Mode}; @@ -51,7 +50,7 @@ fn main() { speed: 2.5, use_frame_interpolation: true, autoplay: true, - segments: Some(vec![10.0, 45.0]), + segments: vec![10.0, 45.0], }); lottie_player.load_animation_path(path.to_str().unwrap(), WIDTH as u32, HEIGHT as u32); diff --git a/deps/modules/emsdk b/deps/modules/emsdk index 8822664a..0bbae749 160000 --- a/deps/modules/emsdk +++ b/deps/modules/emsdk @@ -1 +1 @@ -Subproject commit 8822664a145391eee7cb57f9db7ba3705e19f2fe +Subproject commit 0bbae74935d57ff41739648c12cf90b56668398f diff --git a/dotlottie-ffi/emscripten_bindings.cpp b/dotlottie-ffi/emscripten_bindings.cpp index 2b25b799..8c353d9e 100644 --- a/dotlottie-ffi/emscripten_bindings.cpp +++ b/dotlottie-ffi/emscripten_bindings.cpp @@ -13,6 +13,10 @@ val buffer(DotLottiePlayer &player) EMSCRIPTEN_BINDINGS(DotLottiePlayer) { + + // Register std::vector as VectorFloat for the Config::segments field + register_vector("VectorFloat"); + enum_("Mode") .value("Forward", Mode::FORWARD) .value("Reverse", Mode::REVERSE) @@ -24,7 +28,8 @@ EMSCRIPTEN_BINDINGS(DotLottiePlayer) .field("loop_animation", &Config::loop_animation) .field("mode", &Config::mode) .field("speed", &Config::speed) - .field("use_frame_interpolation", &Config::use_frame_interpolation); + .field("use_frame_interpolation", &Config::use_frame_interpolation) + .field("segments", &Config::segments); class_("DotLottiePlayer") .constructor(&DotLottiePlayer::init, allow_raw_pointers()) diff --git a/dotlottie-ffi/src/dotlottie_player.udl b/dotlottie-ffi/src/dotlottie_player.udl index 6928d7e5..7472d83b 100644 --- a/dotlottie-ffi/src/dotlottie_player.udl +++ b/dotlottie-ffi/src/dotlottie_player.udl @@ -14,7 +14,7 @@ dictionary Config { Mode mode; f32 speed; boolean use_frame_interpolation; - sequence? segments; + sequence segments; }; interface DotLottiePlayer { diff --git a/dotlottie-rs/src/dotlottie_player.rs b/dotlottie-rs/src/dotlottie_player.rs index cd31101e..0bf15a17 100644 --- a/dotlottie-rs/src/dotlottie_player.rs +++ b/dotlottie-rs/src/dotlottie_player.rs @@ -24,7 +24,7 @@ pub struct Config { pub speed: f32, pub use_frame_interpolation: bool, pub autoplay: bool, - pub segments: Option>, + pub segments: Vec, } struct DotLottieRuntime { @@ -49,16 +49,18 @@ impl DotLottieRuntime { } fn start_frame(&self) -> f32 { - match &self.config.segments { - Some(segments) => segments[0], - None => 0.0, + if self.config.segments.len() == 2 { + self.config.segments[0] + } else { + 0.0 } } fn end_frame(&self) -> f32 { - match &self.config.segments { - Some(segments) => segments[1], - None => self.total_frames(), + if self.config.segments.len() == 2 { + self.config.segments[1] + } else { + self.total_frames() } } @@ -151,44 +153,44 @@ impl DotLottieRuntime { // the effective duration in milliseconds (considering the segments & speed) let effective_duration = - (1000.0 * duration * effective_total_frames / total_frames) / self.config.speed; + (duration * effective_total_frames / total_frames) / self.config.speed; - let raw_next_frame = elapsed_time / effective_duration * effective_total_frames; + let raw_next_frame = (elapsed_time / effective_duration) * effective_total_frames; - let next_frame = if self.config.use_frame_interpolation { - raw_next_frame - } else { - raw_next_frame.round() + let next_frame = match self.config.mode { + Mode::Forward => start_frame + raw_next_frame, + Mode::Reverse => end_frame - raw_next_frame, + _ => raw_next_frame, }; - let next_frame = match self.config.mode { - Mode::Forward => next_frame, - Mode::Reverse => effective_total_frames - next_frame, - _ => next_frame, + let next_frame = if self.config.use_frame_interpolation { + next_frame + } else { + next_frame.round() }; let next_frame = match self.config.mode { Mode::Forward => { - if next_frame >= effective_total_frames { + if next_frame >= end_frame { if self.config.loop_animation { self.loop_count += 1; self.start_time = Instant::now(); - 0.0 + start_frame } else { - effective_total_frames + end_frame } } else { next_frame } } Mode::Reverse => { - if next_frame <= 0.0 { + if next_frame <= start_frame { if self.config.loop_animation { self.loop_count += 1; self.start_time = Instant::now(); - effective_total_frames + end_frame } else { - 0.0 + start_frame } } else { next_frame diff --git a/web-example.html b/web-example.html index 915e6de9..cfa8c08c 100644 --- a/web-example.html +++ b/web-example.html @@ -21,12 +21,22 @@ }, }); + function create_Segments(start_frame, end_frame) { + const vector = new Module.VectorFloat(); + + vector.push_back(start_frame); + vector.push_back(end_frame); + + return vector; + } + const dotLottie = new Module.DotLottiePlayer({ autoplay: true, loop_animation: true, mode: Module.Mode.values[1], speed: 1, use_frame_interpolation: true, + segments: create_Segments(0, 22), }); const data = await fetch( @@ -50,6 +60,7 @@ function animationLoop() { const next_frame_number = dotLottie.request_frame(); + console.log(next_frame_number); dotLottie.set_frame(next_frame_number); const rendered = dotLottie.render(); From e29c61bc065a76e276ca1696c4d37387b990242e Mon Sep 17 00:00:00 2001 From: Abdelrahman Ashraf Date: Wed, 17 Jan 2024 12:40:15 +0700 Subject: [PATCH 3/5] =?UTF-8?q?chore:=20=F0=9F=A4=96=20ensure=20next=5Ffra?= =?UTF-8?q?me=20won't=20pass=20start=20&=20end=20frames?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dotlottie-rs/src/dotlottie_player.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dotlottie-rs/src/dotlottie_player.rs b/dotlottie-rs/src/dotlottie_player.rs index 20b0c277..165e711f 100644 --- a/dotlottie-rs/src/dotlottie_player.rs +++ b/dotlottie-rs/src/dotlottie_player.rs @@ -183,6 +183,9 @@ impl DotLottieRuntime { next_frame.round() }; + // to ensure the next_frame won't go beyond the start & end frames + let next_frame = next_frame.clamp(start_frame, end_frame); + let next_frame = match self.config.mode { Mode::Forward => self.handle_forward_mode(next_frame, start_frame, end_frame), Mode::Reverse => self.handle_reverse_mode(next_frame, start_frame, end_frame), From 11630731b4225aab28a65d0985689a83b7bce500 Mon Sep 17 00:00:00 2001 From: Abdelrahman Ashraf Date: Wed, 17 Jan 2024 14:28:55 +0700 Subject: [PATCH 4/5] =?UTF-8?q?chore:=20=F0=9F=A4=96=20update=20web=20exam?= =?UTF-8?q?ple=20+=20cleanup?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- android-cross.txt | 16 ---------------- ios-cross.txt | 20 -------------------- web-example.html | 2 +- 3 files changed, 1 insertion(+), 37 deletions(-) delete mode 100644 android-cross.txt delete mode 100644 ios-cross.txt diff --git a/android-cross.txt b/android-cross.txt deleted file mode 100644 index aa909286..00000000 --- a/android-cross.txt +++ /dev/null @@ -1,16 +0,0 @@ -[binaries] -cpp = 'CPP:' -ar = 'AR:' -as = 'AS:' -ranlib = 'RANLIB:' -ld = 'LD:' -strip = 'STRIP:' - -[properties] -sys_root = 'SYSROOT:' - -[host_machine] -system = 'android' -cpu_family = 'CPU_FAMILY:' -cpu = 'CPU:' -endian = 'little' diff --git a/ios-cross.txt b/ios-cross.txt deleted file mode 100644 index a743023f..00000000 --- a/ios-cross.txt +++ /dev/null @@ -1,20 +0,0 @@ -[binaries] -cpp = ['clang++', '-arch', 'ARCH:', '-isysroot', 'SYSROOT:'] -ar = 'ar' -strip = 'strip' - -[properties] -root = 'SDKROOT:' -has_function_printf = true - -[built-in options] -cpp_args = ['-miphoneos-version-min=11.0'] -cpp_link_args = ['-miphoneos-version-min=11.0'] - -[host_machine] -system = 'darwin' -subsystem = 'ios' -kernel = 'xnu' -cpu_family = 'CPU_FAMILY:' -cpu = 'CPU:' -endian = 'little' diff --git a/web-example.html b/web-example.html index b13c4bce..027c12bc 100644 --- a/web-example.html +++ b/web-example.html @@ -36,7 +36,7 @@ mode: Module.Mode.values[3], speed: 1, use_frame_interpolation: true, - segments: create_Segments(0, 22), + segments: create_Segments(0, 40), }); const data = await fetch( From 8705c106670aa8b5fa893f249fcbf160eac23627 Mon Sep 17 00:00:00 2001 From: Abdelrahman Ashraf Date: Wed, 17 Jan 2024 20:46:35 +0700 Subject: [PATCH 5/5] =?UTF-8?q?fix:=20=F0=9F=90=9B=20normalize=20start=20&?= =?UTF-8?q?=20end=20frame=20when=20>=20or=20<=20max=20or=20zero?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dotlottie-rs/src/dotlottie_player.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dotlottie-rs/src/dotlottie_player.rs b/dotlottie-rs/src/dotlottie_player.rs index 165e711f..f6e1e671 100644 --- a/dotlottie-rs/src/dotlottie_player.rs +++ b/dotlottie-rs/src/dotlottie_player.rs @@ -64,7 +64,7 @@ impl DotLottieRuntime { fn start_frame(&self) -> f32 { if self.config.segments.len() == 2 { - self.config.segments[0] + self.config.segments[0].max(0.0) } else { 0.0 } @@ -72,7 +72,7 @@ impl DotLottieRuntime { fn end_frame(&self) -> f32 { if self.config.segments.len() == 2 { - self.config.segments[1] + self.config.segments[1].min(self.total_frames()) } else { self.total_frames() }