diff --git a/dotlottie-rs/src/dotlottie_player.rs b/dotlottie-rs/src/dotlottie_player.rs index 10282669..d4b6651f 100644 --- a/dotlottie-rs/src/dotlottie_player.rs +++ b/dotlottie-rs/src/dotlottie_player.rs @@ -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 { @@ -16,67 +11,64 @@ pub struct DotLottiePlayer { speed: i32, direction: AtomicI8, - // Animation information related - current_frame: Arc>, - total_frames: Arc>, - duration: Arc>, - // Data - animation: Arc>, - canvas: Arc>, + animation: thorvg::Animation, + canvas: thorvg::Canvas, buffer: Mutex>, } 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 { @@ -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 @@ -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) +} diff --git a/dotlottie-rs/src/lib.rs b/dotlottie-rs/src/lib.rs index 67fd7ad9..223ed861 100644 --- a/dotlottie-rs/src/lib.rs +++ b/dotlottie-rs/src/lib.rs @@ -1,5 +1,7 @@ mod dotlottie_player; mod errors; +mod thorvg; pub use crate::dotlottie_player::*; pub use crate::errors::*; +pub use crate::thorvg::*; diff --git a/dotlottie-rs/src/thorvg.rs b/dotlottie-rs/src/thorvg.rs new file mode 100644 index 00000000..76e112ea --- /dev/null +++ b/dotlottie-rs/src/thorvg.rs @@ -0,0 +1,270 @@ +#![allow(non_upper_case_globals)] + +use std::ffi::CString; + +include!(concat!(env!("OUT_DIR"), "/bindings.rs")); + +#[derive(Debug)] +pub enum TvgError { + InvalidArgument, + InsufficientCondition, + FailedAllocation, + MemoryCorruption, + NotSupported, + Unknown, +} + +pub enum TvgEngine { + TvgEngineSw, + TvgEngineGl, +} + +pub enum TvgColorspace { + ABGR8888, + ARGB8888, +} + +fn convert_tvg_result(result: Tvg_Result) -> Result<(), TvgError> { + match result { + Tvg_Result_TVG_RESULT_SUCCESS => Ok(()), + Tvg_Result_TVG_RESULT_INVALID_ARGUMENT => Err(TvgError::InvalidArgument), + Tvg_Result_TVG_RESULT_INSUFFICIENT_CONDITION => Err(TvgError::InsufficientCondition), + Tvg_Result_TVG_RESULT_FAILED_ALLOCATION => Err(TvgError::FailedAllocation), + Tvg_Result_TVG_RESULT_MEMORY_CORRUPTION => Err(TvgError::MemoryCorruption), + Tvg_Result_TVG_RESULT_NOT_SUPPORTED => Err(TvgError::NotSupported), + Tvg_Result_TVG_RESULT_UNKNOWN | _ => Err(TvgError::Unknown), + } +} + +pub struct Canvas { + raw_canvas: *mut Tvg_Canvas, +} + +impl Canvas { + pub fn new(engine_method: TvgEngine, threads: u32) -> Self { + Canvas { + raw_canvas: unsafe { + let engine_method = match engine_method { + TvgEngine::TvgEngineSw => Tvg_Engine_TVG_ENGINE_SW, + TvgEngine::TvgEngineGl => Tvg_Engine_TVG_ENGINE_GL, + }; + + tvg_engine_init(engine_method, threads); + + tvg_swcanvas_create() + }, + } + } + + pub fn set_target( + &self, + buffer: *mut u32, + stride: u32, + width: u32, + height: u32, + color_space: TvgColorspace, + ) -> Result<(), TvgError> { + let color_space = match color_space { + TvgColorspace::ABGR8888 => Tvg_Colorspace_TVG_COLORSPACE_ABGR8888, + TvgColorspace::ARGB8888 => Tvg_Colorspace_TVG_COLORSPACE_ARGB8888, + }; + + let result = unsafe { + tvg_swcanvas_set_target(self.raw_canvas, buffer, stride, width, height, color_space) + }; + + convert_tvg_result(result) + } + + pub fn clear(&self, paints: bool, buffer: bool) -> Result<(), TvgError> { + let result = unsafe { tvg_canvas_clear(self.raw_canvas, paints, buffer) }; + + convert_tvg_result(result) + } + + pub fn push(&self, picture: &Picture) -> Result<(), TvgError> { + let result = unsafe { tvg_canvas_push(self.raw_canvas, picture.raw_paint()) }; + + convert_tvg_result(result) + } + + pub fn draw(&self) -> Result<(), TvgError> { + let result = unsafe { tvg_canvas_draw(self.raw_canvas) }; + + convert_tvg_result(result) + } + + pub fn sync(&self) -> Result<(), TvgError> { + let result = unsafe { tvg_canvas_sync(self.raw_canvas) }; + + convert_tvg_result(result) + } + + pub fn update(&self) -> Result<(), TvgError> { + let result = unsafe { tvg_canvas_update(self.raw_canvas) }; + + convert_tvg_result(result) + } + + pub fn set_mempool(&self, policy: Tvg_Mempool_Policy) -> Result<(), TvgError> { + let result = unsafe { tvg_swcanvas_set_mempool(self.raw_canvas, policy) }; + + convert_tvg_result(result) + } +} + +impl Drop for Canvas { + fn drop(&mut self) { + unsafe { + tvg_canvas_destroy(self.raw_canvas); + } + } +} + +pub struct Picture { + raw_paint: *mut Tvg_Paint, +} + +impl Picture { + pub fn from_raw(raw: *mut Tvg_Paint) -> Self { + Picture { raw_paint: raw } + } + + fn raw_paint(&self) -> *mut Tvg_Paint { + self.raw_paint + } + + pub fn load(&self, path: &str) -> Result<(), TvgError> { + let path = CString::new(path).expect("Failed to create CString"); + + let result = unsafe { tvg_picture_load(self.raw_paint, path.as_ptr()) }; + + convert_tvg_result(result) + } + + pub fn load_data(&self, data: &[u8], mimetype: &str) -> Result<(), TvgError> { + let mimetype = CString::new(mimetype).expect("Failed to create CString"); + + let result = unsafe { + tvg_picture_load_data( + self.raw_paint, + data.as_ptr() as *const std::os::raw::c_char, + data.len() as u32, + mimetype.as_ptr(), + std::ptr::null(), + false, + ) + }; + + convert_tvg_result(result) + } + + pub fn get_size(&self) -> Result<(f32, f32), TvgError> { + let mut width: f32 = 0.0; + let mut height: f32 = 0.0; + + let result = unsafe { + tvg_picture_get_size( + self.raw_paint, + &mut width as *mut f32, + &mut height as *mut f32, + ) + }; + + convert_tvg_result(result)?; + + Ok((width, height)) + } + + pub fn set_size(&self, width: f32, height: f32) -> Result<(), TvgError> { + let result = unsafe { tvg_picture_set_size(self.raw_paint, width, height) }; + + convert_tvg_result(result) + } + + pub fn scale(&self, factor: f32) -> Result<(), TvgError> { + let result = unsafe { tvg_paint_scale(self.raw_paint(), factor) }; + + convert_tvg_result(result) + } + + pub fn translate(&self, tx: f32, ty: f32) -> Result<(), TvgError> { + let result = unsafe { tvg_paint_translate(self.raw_paint(), tx, ty) }; + + convert_tvg_result(result) + } + + pub fn rotate(&self, degree: f32) -> Result<(), TvgError> { + let result = unsafe { tvg_paint_rotate(self.raw_paint(), degree) }; + + convert_tvg_result(result) + } +} + +pub struct Animation { + raw_animation: *mut Tvg_Animation, +} + +impl Animation { + pub fn new() -> Self { + Animation { + raw_animation: unsafe { tvg_animation_new() }, + } + } + + pub fn get_picture(&self) -> Option { + let raw_picture = unsafe { tvg_animation_get_picture(self.raw_animation) }; + if !raw_picture.is_null() { + Some(Picture::from_raw(raw_picture)) + } else { + None + } + } + + pub fn get_total_frame(&self) -> Result { + let mut total_frame: f32 = 0.0; + + let result = unsafe { + tvg_animation_get_total_frame(self.raw_animation, &mut total_frame as *mut f32) + }; + + convert_tvg_result(result)?; + + return Ok(total_frame); + } + + pub fn get_duration(&self) -> Result { + let mut duration: f32 = 0.0; + + let result = + unsafe { tvg_animation_get_duration(self.raw_animation, &mut duration as *mut f32) }; + + convert_tvg_result(result)?; + + return Ok(duration); + } + + pub fn set_frame(&self, frame_no: f32) -> Result<(), TvgError> { + let result = unsafe { tvg_animation_set_frame(self.raw_animation, frame_no) }; + + convert_tvg_result(result) + } + + pub fn get_frame(&self) -> Result { + let mut curr_frame: f32 = 0.0; + let result = + unsafe { tvg_animation_get_frame(self.raw_animation, &mut curr_frame as *mut f32) }; + + convert_tvg_result(result)?; + + Ok(curr_frame) + } +} + +impl Drop for Animation { + fn drop(&mut self) { + unsafe { + tvg_animation_del(self.raw_animation); + } + } +}