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: 🎸 safe thorvg wrapper #19

Merged
merged 4 commits into from
Dec 14, 2023
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
271 changes: 101 additions & 170 deletions dotlottie-rs/src/dotlottie_player.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]

include!(concat!(env!("OUT_DIR"), "/bindings.rs"));

use std::ffi::CString;
use std::sync::atomic::AtomicI8;
use std::sync::{Arc, Mutex, RwLock};
use std::sync::Mutex;

use crate::thorvg;

#[allow(dead_code)]
pub struct DotLottiePlayer {
Expand All @@ -16,67 +11,64 @@ pub struct DotLottiePlayer {
speed: i32,
direction: AtomicI8,

// Animation information related
current_frame: Arc<RwLock<f32>>,
total_frames: Arc<RwLock<f32>>,
duration: Arc<RwLock<f32>>,

// Data
animation: Arc<RwLock<*mut Tvg_Animation>>,
canvas: Arc<RwLock<*mut Tvg_Canvas>>,
animation: thorvg::Animation,
canvas: thorvg::Canvas,
buffer: Mutex<Vec<u32>>,
}

impl DotLottiePlayer {
pub fn new() -> Self {
let canvas = thorvg::Canvas::new(thorvg::TvgEngine::TvgEngineSw, 0);
let animation = thorvg::Animation::new();
let buffer = Mutex::new(vec![]);

DotLottiePlayer {
autoplay: false,
loop_animation: false,
speed: 1,
direction: AtomicI8::new(1),
current_frame: Arc::new(RwLock::new(0.0)),

total_frames: Arc::new(RwLock::new(0.0)),
duration: Arc::new(RwLock::new(0.0)),
animation: Arc::new(RwLock::new(std::ptr::null_mut())),
canvas: Arc::new(RwLock::new(std::ptr::null_mut())),
buffer: Mutex::new(vec![]),
// For some reason initializing here doesn't work
// animation: tvg_animation_new(),
// canvas: tvg_swcanvas_create(),
animation,
canvas,
buffer,
}
}

pub fn frame(&self, no: f32) {
unsafe {
let current_frame = &mut *self.current_frame.write().unwrap();
let animation = self.animation.read().unwrap().as_mut().unwrap();
let canvas = self.canvas.read().unwrap().as_mut().unwrap();
self.canvas.clear(false, true);

*current_frame = no;
self.animation.set_frame(no);

tvg_canvas_clear(canvas, false, true);

tvg_animation_set_frame(animation, *current_frame);

tvg_canvas_update(canvas);

tvg_canvas_draw(canvas);

tvg_canvas_sync(canvas);
}
self.canvas.update();
self.canvas.draw();
self.canvas.sync();
}

pub fn get_total_frame(&self) -> f32 {
return self.total_frames.read().unwrap().clone();
let total_frames = self.animation.get_total_frame();

match total_frames {
Ok(total_frames) => total_frames,
Err(_) => 0.0,
}
}

pub fn get_duration(&self) -> f32 {
return self.duration.read().unwrap().clone();
let duration = self.animation.get_duration();

match duration {
Ok(duration) => duration,
Err(_) => 0.0,
}
}

pub fn get_current_frame(&self) -> f32 {
return self.current_frame.read().unwrap().clone();
let result = self.animation.get_frame();

match result {
Ok(frame) => frame,
Err(_) => 0.0,
}
}

pub fn get_buffer(&self) -> i64 {
Expand All @@ -91,157 +83,83 @@ impl DotLottiePlayer {
}

pub fn clear(&self) {
unsafe {
let canvas = self.canvas.read().unwrap().as_mut().unwrap();

tvg_canvas_clear(canvas, false, true);
}
self.canvas.clear(false, true);
}

pub fn load_animation_from_path(&self, path: &str, width: u32, height: u32) -> bool {
unsafe {
tvg_engine_init(Tvg_Engine_TVG_ENGINE_SW, 0);

*self.canvas.write().unwrap() = tvg_swcanvas_create();

let canvas = self.canvas.read().unwrap().as_mut().unwrap();
let mut buffer_lock = self.buffer.lock().unwrap();

let mut buffer_lock = self.buffer.lock().unwrap();
*buffer_lock = vec![0; (width * height * 4) as usize];

*buffer_lock = vec![0; (width * height * 4) as usize];
self.canvas.set_target(
buffer_lock.as_ptr() as *mut u32,
width,
width,
height,
thorvg::TvgColorspace::ABGR8888,
);

// self.buffer.as = vec![width * height];

tvg_swcanvas_set_target(
canvas,
buffer_lock.as_ptr() as *mut u32,
width,
width,
height,
Tvg_Colorspace_TVG_COLORSPACE_ABGR8888,
);
if let Some(frame_image) = self.animation.get_picture() {
if frame_image.load(path).is_err() {
return false;
}

*self.animation.write().unwrap() = tvg_animation_new();
let (pw, ph) = frame_image.get_size().unwrap();

let animation = self.animation.read().unwrap().as_mut().unwrap();
let (scale, shift_x, shift_y) = calculate_scale_and_shift(pw, ph, width, height);

let frame_image = tvg_animation_get_picture(animation);
frame_image.scale(scale);
frame_image.translate(shift_x, shift_y);

let load_result =
tvg_picture_load(frame_image, path.as_ptr() as *const std::os::raw::c_char);
self.canvas.push(&frame_image);

if load_result != Tvg_Result_TVG_RESULT_SUCCESS {
tvg_animation_del(animation);
self.animation.set_frame(0.0);

return false;
} else {
let total_frames = &mut *self.total_frames.write().unwrap();
let duration = &mut *self.duration.write().unwrap();
let mut pw: f32 = 0.0;
let mut ph: f32 = 0.0;
let scale: f32;
let mut shiftY: f32 = 0.0;
let mut shiftX: f32 = 0.0;

tvg_picture_get_size(frame_image, &mut pw as *mut f32, &mut ph as *mut f32);

if pw > ph {
scale = width as f32 / pw;
shiftY = (height as f32 / ph * scale) * 0.5;
} else {
scale = height as f32 / ph;
shiftX = (width as f32 - pw * scale) * 0.5;
}

tvg_paint_scale(frame_image, scale);
tvg_paint_translate(frame_image, shiftX, shiftY);

tvg_animation_get_total_frame(animation, total_frames as *mut f32);
tvg_animation_get_duration(animation, duration as *mut f32);
tvg_animation_set_frame(animation, 0.0);
tvg_canvas_push(canvas, frame_image);
tvg_canvas_draw(canvas);
tvg_canvas_sync(canvas);
}
self.canvas.draw();
self.canvas.sync();
} else {
return false;
}

true
}

pub fn load_animation(&self, animation_data: &str, width: u32, height: u32) -> bool {
let mimetype = CString::new("lottie").expect("Failed to create CString");

unsafe {
tvg_engine_init(Tvg_Engine_TVG_ENGINE_SW, 0);

*self.canvas.write().unwrap() = tvg_swcanvas_create();

let canvas = self.canvas.read().unwrap().as_mut().unwrap();

let mut buffer_lock = self.buffer.lock().unwrap();

*buffer_lock = vec![0; (width * height * 4) as usize];

tvg_swcanvas_set_target(
canvas,
buffer_lock.as_ptr() as *mut u32,
width,
width,
height,
Tvg_Colorspace_TVG_COLORSPACE_ABGR8888,
);

*self.animation.write().unwrap() = tvg_animation_new();
let mut buffer_lock = self.buffer.lock().unwrap();

*buffer_lock = vec![0; (width * height * 4) as usize];

self.canvas.set_target(
buffer_lock.as_ptr() as *mut u32,
width,
width,
height,
thorvg::TvgColorspace::ABGR8888,
);

if let Some(mut frame_image) = self.animation.get_picture() {
if frame_image
.load_data(animation_data.as_bytes(), "lottie")
.is_err()
{
return false;
}

let animation = self.animation.read().unwrap().as_mut().unwrap();
let (pw, ph) = frame_image.get_size().unwrap();

let frame_image = tvg_animation_get_picture(animation);
let (scale, shift_x, shift_y) = calculate_scale_and_shift(pw, ph, width, height);

// resource path (null if not needed)
let rpath = std::ptr::null();
frame_image.scale(scale);
frame_image.translate(shift_x, shift_y);

let load_result = tvg_picture_load_data(
frame_image,
animation_data.as_ptr() as *const std::os::raw::c_char,
animation_data.len() as u32,
mimetype.as_ptr(),
rpath,
false,
);
self.canvas.push(&frame_image);

if load_result != Tvg_Result_TVG_RESULT_SUCCESS {
tvg_animation_del(animation);
self.animation.set_frame(0.0);

return false;
} else {
let total_frames = &mut *self.total_frames.write().unwrap();
let duration = &mut *self.duration.write().unwrap();
let mut pw: f32 = 0.0;
let mut ph: f32 = 0.0;
let scale: f32;
let mut shiftY: f32 = 0.0;
let mut shiftX: f32 = 0.0;

tvg_picture_get_size(frame_image, &mut pw as *mut f32, &mut ph as *mut f32);

if pw > ph {
scale = width as f32 / pw;
shiftY = (height as f32 / ph * scale) * 0.5;
} else {
scale = height as f32 / ph;
shiftX = (width as f32 - pw * scale) * 0.5;
}

tvg_paint_scale(frame_image, scale);
tvg_paint_translate(frame_image, shiftX, shiftY);

tvg_animation_get_total_frame(animation, total_frames as *mut f32);
tvg_animation_get_duration(animation, duration as *mut f32);
tvg_animation_set_frame(animation, 0.0);
tvg_canvas_push(canvas, frame_image);
tvg_canvas_draw(canvas);
tvg_canvas_sync(canvas);
}
self.canvas.draw();
self.canvas.sync();
} else {
return false;
}

true
Expand All @@ -250,3 +168,16 @@ impl DotLottiePlayer {

unsafe impl Send for DotLottiePlayer {}
unsafe impl Sync for DotLottiePlayer {}

fn calculate_scale_and_shift(pw: f32, ph: f32, width: u32, height: u32) -> (f32, f32, f32) {
let scale = if pw > ph {
width as f32 / pw
} else {
height as f32 / ph
};

let shift_x = (width as f32 - pw * scale) / 2.0;
let shift_y = (height as f32 - ph * scale) / 2.0;

(scale, shift_x, shift_y)
}
2 changes: 2 additions & 0 deletions dotlottie-rs/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
mod dotlottie_player;
mod errors;
mod thorvg;

pub use crate::dotlottie_player::*;
pub use crate::errors::*;
pub use crate::thorvg::*;
Loading