Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: 🎸 segments #45

Merged
merged 7 commits into from
Jan 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
16 changes: 0 additions & 16 deletions android-cross.txt

This file was deleted.

1 change: 1 addition & 0 deletions demo-player/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ fn main() {
speed: 1.0,
use_frame_interpolation: true,
autoplay: true,
segments: vec![10.0, 45.0],
});
lottie_player.load_animation_path(path.to_str().unwrap(), WIDTH as u32, HEIGHT as u32);

Expand Down
2 changes: 1 addition & 1 deletion deps/modules/emsdk
7 changes: 6 additions & 1 deletion dotlottie-ffi/emscripten_bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ val buffer(DotLottiePlayer &player)

EMSCRIPTEN_BINDINGS(DotLottiePlayer)
{

// Register std::vector<float> as VectorFloat for the Config::segments field
register_vector<float>("VectorFloat");

enum_<Mode>("Mode")
.value("Forward", Mode::FORWARD)
.value("Reverse", Mode::REVERSE)
Expand All @@ -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>("DotLottiePlayer")
.constructor(&DotLottiePlayer::init, allow_raw_pointers())
Expand Down
1 change: 1 addition & 0 deletions dotlottie-ffi/src/dotlottie_player.udl
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ dictionary Config {
Mode mode;
f32 speed;
boolean use_frame_interpolation;
sequence<f32> segments;
};

interface DotLottiePlayer {
Expand Down
130 changes: 84 additions & 46 deletions dotlottie-rs/src/dotlottie_player.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,14 @@ enum Direction {
Reverse,
}

#[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: Vec<f32>,
}

struct DotLottieRuntime {
Expand Down Expand Up @@ -61,6 +62,22 @@ impl DotLottieRuntime {
}
}

fn start_frame(&self) -> f32 {
if self.config.segments.len() == 2 {
self.config.segments[0].max(0.0)
} else {
0.0
}
}

fn end_frame(&self) -> f32 {
if self.config.segments.len() == 2 {
self.config.segments[1].min(self.total_frames())
} else {
self.total_frames()
}
}

pub fn is_loaded(&self) -> bool {
self.is_loaded
}
Expand Down Expand Up @@ -109,8 +126,9 @@ impl DotLottieRuntime {
pub fn stop(&mut self) -> bool {
if self.is_loaded {
self.playback_state = PlaybackState::Stopped;
let start_frame = 0_f32;
let end_frame = self.total_frames();

let start_frame = self.start_frame();
let end_frame = self.end_frame();

match self.config.mode {
Mode::Forward | Mode::Bounce => {
Expand All @@ -132,118 +150,137 @@ impl DotLottieRuntime {
return self.current_frame();
}

let elapsed_time = self.start_time.elapsed().as_secs_f32() * 1000.0;
let elapsed_time = self.start_time.elapsed().as_secs_f32();

let duration = (self.duration() * 1000.0) / self.config.speed as f32;
// the animation total frames
let total_frames = self.total_frames();

let raw_next_frame = (elapsed_time / duration) * total_frames;
// the animation duration in seconds
let duration = self.duration();

let next_frame = if self.config.use_frame_interpolation {
raw_next_frame
} else {
raw_next_frame.round()
};
// 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;

// the effective duration in milliseconds (considering the segments & speed)
let effective_duration =
(duration * effective_total_frames / total_frames) / self.config.speed;

let raw_next_frame = (elapsed_time / effective_duration) * effective_total_frames;

// update the next frame based on the direction
let next_frame = match self.direction {
Direction::Forward => next_frame,
Direction::Reverse => total_frames - next_frame,
Direction::Forward => start_frame + raw_next_frame,
Direction::Reverse => end_frame - raw_next_frame,
};

let next_frame = if self.config.use_frame_interpolation {
next_frame
} else {
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),
Mode::Reverse => self.handle_reverse_mode(next_frame),
Mode::Bounce => self.handle_bounce_mode(next_frame),
Mode::ReverseBounce => self.handle_reverse_bounce_mode(next_frame),
Mode::Forward => self.handle_forward_mode(next_frame, start_frame, end_frame),
Mode::Reverse => self.handle_reverse_mode(next_frame, start_frame, end_frame),
Mode::Bounce => self.handle_bounce_mode(next_frame, start_frame, end_frame),
Mode::ReverseBounce => {
self.handle_reverse_bounce_mode(next_frame, start_frame, end_frame)
}
};

next_frame
}

fn handle_forward_mode(&mut self, next_frame: f32) -> f32 {
let total_frames = self.total_frames();

if next_frame >= total_frames {
fn handle_forward_mode(&mut self, next_frame: f32, start_frame: f32, end_frame: f32) -> f32 {
if next_frame >= end_frame {
if self.config.loop_animation {
self.loop_count += 1;
self.start_time = Instant::now();

0.0
start_frame
} else {
total_frames
end_frame
}
} else {
next_frame
}
}

fn handle_reverse_mode(&mut self, next_frame: f32) -> f32 {
let total_frames = self.total_frames();
if next_frame <= 0.0 {
fn handle_reverse_mode(&mut self, next_frame: f32, start_frame: f32, end_frame: f32) -> f32 {
if next_frame <= start_frame {
if self.config.loop_animation {
self.loop_count += 1;
self.start_time = Instant::now();
total_frames

end_frame
} else {
0.0
start_frame
}
} else {
next_frame
}
}

fn handle_bounce_mode(&mut self, next_frame: f32) -> f32 {
let total_frames = self.total_frames();

fn handle_bounce_mode(&mut self, next_frame: f32, start_frame: f32, end_frame: f32) -> f32 {
match self.direction {
Direction::Forward => {
if next_frame >= total_frames {
if next_frame >= end_frame {
self.direction = Direction::Reverse;
self.start_time = Instant::now();
total_frames

end_frame
} else {
next_frame
}
}
Direction::Reverse => {
if next_frame <= 0.0 {
if next_frame <= start_frame {
if self.config.loop_animation {
self.loop_count += 1;
self.direction = Direction::Forward;
self.start_time = Instant::now();
}

0.0
start_frame
} else {
next_frame
}
}
}
}

fn handle_reverse_bounce_mode(&mut self, next_frame: f32) -> f32 {
let total_frames = self.total_frames();

fn handle_reverse_bounce_mode(
&mut self,
next_frame: f32,
start_frame: f32,
end_frame: f32,
) -> f32 {
match self.direction {
Direction::Reverse => {
if next_frame <= 0.0 {
if next_frame <= start_frame {
self.direction = Direction::Forward;
self.start_time = Instant::now();
0.0
start_frame
} else {
next_frame
}
}
Direction::Forward => {
if next_frame >= total_frames {
if next_frame >= end_frame {
if self.config.loop_animation {
self.loop_count += 1;
self.direction = Direction::Reverse;
self.start_time = Instant::now();
}

total_frames
end_frame
} else {
next_frame
}
Expand Down Expand Up @@ -279,7 +316,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 {
Expand All @@ -303,14 +340,15 @@ impl DotLottieRuntime {
F: FnOnce(&mut LottieRenderer, u32, u32) -> Result<(), LottieRendererError>,
{
let loaded = loader(&mut self.renderer, width, height).is_ok();

self.is_loaded = loaded;

let first_frame = 0_f32;
let end_frame = self.total_frames();
let start_frame = self.start_frame();
let end_frame = self.end_frame();

match self.config.mode {
Mode::Forward | Mode::Bounce => {
self.set_frame(first_frame);
self.set_frame(start_frame);
self.direction = Direction::Forward;
}
Mode::Reverse | Mode::ReverseBounce => {
Expand Down
20 changes: 0 additions & 20 deletions ios-cross.txt

This file was deleted.

11 changes: 11 additions & 0 deletions web-example.html
Original file line number Diff line number Diff line change
Expand Up @@ -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[3],
speed: 1,
use_frame_interpolation: true,
segments: create_Segments(0, 40),
});

const data = await fetch(
Expand All @@ -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();

Expand Down
Loading