diff --git a/lottie2gif/src/lib.rs b/lottie2gif/src/lib.rs index 2589c567..6d52b717 100644 --- a/lottie2gif/src/lib.rs +++ b/lottie2gif/src/lib.rs @@ -1,10 +1,11 @@ //! Convert lottie animations to GIF files. #![warn(rust_2018_idioms)] -#![deny(unreachable_pub)] +#![deny(elided_lifetimes_in_paths, unreachable_pub)] use gif::{DisposalMethod, Encoder, EncodingError, Frame, Repeat}; use rgb::{alt::BGRA8, RGBA8}; +use rlottie::Surface; use std::{ fmt::{self, Display, Formatter}, io::Write, @@ -139,16 +140,16 @@ pub fn convert( let size = player.size(); let framerate = player.framerate(); let delay = (100.0 / framerate).round() as u16; - let buffer_len = size.width() as usize * size.height() as usize; - let mut buffer_argb = Vec::with_capacity(buffer_len); + let buffer_len = size.width * size.height; + let mut surface = Surface::new(size); let mut buffer_rgba = vec![RGBA8::default(); buffer_len]; let frame_count = player.totalframe(); - let mut gif = Encoder::new(out, size.width() as _, size.height() as _, &[])?; + let mut gif = Encoder::new(out, size.width as _, size.height as _, &[])?; gif.set_repeat(Repeat::Infinite)?; for frame in 0 .. frame_count { - player.render(frame, &mut buffer_argb, size).unwrap(); - argb_to_rgba(bg, &buffer_argb, &mut buffer_rgba); + player.render(frame, &mut surface); + argb_to_rgba(bg, surface.data(), &mut buffer_rgba); let mut frame = { // Safety: The pointer is valid and aligned since it comes from a vec, and we don't @@ -160,8 +161,8 @@ pub fn convert( ) }; Frame::from_rgba_speed( - size.width() as _, - size.height() as _, + size.width as _, + size.height as _, buffer_rgba, 10 ) diff --git a/lottie2webp/src/lib.rs b/lottie2webp/src/lib.rs index 90639cfc..a0158b47 100644 --- a/lottie2webp/src/lib.rs +++ b/lottie2webp/src/lib.rs @@ -1,8 +1,15 @@ +//! Convert lottie animations to WEBP files. + +#![warn(rust_2018_idioms)] +#![deny(elided_lifetimes_in_paths, unreachable_pub)] + use rgb::{alt::BGRA8, RGBA8}; -use rlottie::Animation; +use rlottie::Surface; use std::slice; use webp_animation::{Encoder, WebPData}; +pub use rlottie::Animation; + #[macro_use] mod util; @@ -21,16 +28,16 @@ pub fn convert(mut player: Animation) -> Result let size = player.size(); let framerate = player.framerate(); let delay = 1000.0 / framerate; - let buffer_len = size.width() as usize * size.height() as usize; - let mut buffer_argb = Vec::with_capacity(buffer_len); + let buffer_len = size.width * size.height; + let mut surface = Surface::new(size); let mut buffer_rgba = vec![RGBA8::default(); buffer_len]; let frame_count = player.totalframe(); - let mut webp = Encoder::new((size.width() as u32, size.height() as u32))?; + let mut webp = Encoder::new((size.width as _, size.height as _))?; let mut timestamp: f64 = 0.0; for frame in 0 .. frame_count { - player.render(frame, &mut buffer_argb, size).unwrap(); - bgra_to_rgba(&buffer_argb, &mut buffer_rgba); + player.render(frame, &mut surface); + bgra_to_rgba(surface.data(), &mut buffer_rgba); { // Safety: The pointer is valid and aligned since it comes from a vec, and we don't diff --git a/rlottie/src/lib.rs b/rlottie/src/lib.rs index 5916a6ea..81ac84f8 100644 --- a/rlottie/src/lib.rs +++ b/rlottie/src/lib.rs @@ -31,14 +31,6 @@ impl Size { pub const fn new(width: usize, height: usize) -> Self { Self { width, height } } - - pub const fn width(&self) -> usize { - self.width - } - - pub const fn height(&self) -> usize { - self.height - } } /// It is very important that [`BGRA8`] and `u32` have exactly the same size. This @@ -67,6 +59,53 @@ mod bgra8_size { }; } +/// A surface has a fixed size and contains pixel data for it. You can render frames onto +/// the surface. +pub struct Surface { + data: Vec, + size: Size +} + +impl Surface { + /// Create a new surface with a fixed size. + pub fn new(size: Size) -> Self { + Self { + data: Vec::with_capacity(size.width * size.height), + size + } + } + + /// Return the size of the surface. + pub fn size(&self) -> Size { + self.size + } + + /// Return the width of the surface. + pub fn width(&self) -> usize { + self.size.width + } + + /// Return the height of the surface. + pub fn height(&self) -> usize { + self.size.height + } + + /// Return the pixel data of the surface. + pub fn data(&self) -> &[BGRA8] { + &self.data + } + + /// Return a pointer to the pixel data. + fn as_mut_ptr(&mut self) -> *mut u32 { + self.data.as_mut_ptr() as *mut u32 + } + + /// Set the length of the pixel data to `width * height`. + unsafe fn set_len(&mut self) { + self.data.set_len(self.width() * self.height()) + } +} + /// A lottie animation. pub struct Animation(*mut Lottie_Animation_S); @@ -147,38 +186,18 @@ impl Animation { unsafe { lottie_animation_get_frame_at_pos(self.0, pos) } } - /// Render the contents of a frame into the buffer at a certain viewport size. - /// - /// The buffer's capacity must be at least `size.width * size.height`. It's - /// initial length or content doesn't matter. The first `size.width * size.height` - /// bytes of the buffer will be written to; and it's length will be set exactly - /// to `size.width * size.height`. - /// - /// This operation will fail only if the buffer's capacity isn't large enough. - pub fn render( - &mut self, - frame_num: usize, - buffer: &mut Vec, - size: Size - ) -> Result<(), RenderError> { - let buffer_len = (size.width * size.height) as usize; - if buffer.capacity() < buffer_len { - return Err(RenderError); - } + /// Render the contents of a frame onto the surface. + pub fn render(&mut self, frame_num: usize, surface: &mut Surface) { unsafe { lottie_animation_render( self.0, frame_num, - buffer.as_mut_ptr() as *mut u32, - size.width, - size.height, - size.width * 4 + surface.as_mut_ptr(), + surface.width(), + surface.height(), + surface.width() * 4 ); - buffer.set_len(buffer_len); + surface.set_len(); } - Ok(()) } } - -#[derive(Debug)] -pub struct RenderError;