Skip to content

Commit

Permalink
Add resource loader from buffer and Read trait (#25)
Browse files Browse the repository at this point in the history
  • Loading branch information
r12f authored Jul 24, 2022
1 parent 88de34c commit 0968fd2
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 49 deletions.
13 changes: 10 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,13 @@ clock

## How to use

To use this library, please add the following into the dependencies in the `Cargo.toml` file:

```toml
[dependencies]
divoom = "0.1"
```

The library contains 2 major parts:

- Divoom service APIs, that is used for talking to Divoom's backend service for device discovery etc.
Expand Down Expand Up @@ -131,7 +138,7 @@ all the GIF frames into it one by one. To help with this process, we created a r
use divoom::*;

// Load the resource.
let frames = DivoomAnimationResourceLoader::gif("test_data/animation_builder_tests/logo-16-rotate-4-frames.gif").unwrap();
let frames = DivoomAnimationResourceLoader::gif_file("test_data/animation_builder_tests/logo-16-rotate-4-frames.gif").unwrap();

// Build animation with 16 pixel canvas and 100ms frame play speed.
let builder = DivoomAnimationBuilder::new(16, Duration::from_millis(100)).unwrap();
Expand All @@ -150,9 +157,9 @@ let pixoo = PixooClient::new("192.168.0.123");
pixoo.send_gif_as_animation(16, Duration::from_millis(100), "test_data/animation_builder_tests/logo-16-rotate-4-frames.gif").await
```

For more on how to use it, feel free to check our doc here: <https://docs.rs/divoom/latest/divoom/struct.DivoomAnimationBuilder.html>.
Besides gif, we also support png and jpeg format. And besides reading from file, we also support loading resource from any `Read` trait. For more on how to use it, feel free to check our doc here: <https://docs.rs/divoom/latest/divoom/struct.DivoomAnimationBuilder.html>.

And if you don't want this animation builder, we can exclude it by specifying the features with:
And for any reason, if you don't want the builtin animation builder, we can exclude it by specifying the features with:

```toml
[dependencies]
Expand Down
51 changes: 29 additions & 22 deletions divoom/src/animation/animation_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,17 +206,19 @@ mod tests {
let mut frame = frame_builder.canvas_mut();
assert_eq!(frame.width(), 64);

let frames =
DivoomAnimationResourceLoader::gif("test_data/animation_builder_tests/logo-16-0.gif")
.unwrap();
let frames = DivoomAnimationResourceLoader::gif_file(
"test_data/animation_builder_tests/logo-16-0.gif",
)
.unwrap();
frame_builder = frame_builder.draw_frame(&frames[0]);
}

#[test]
fn divoom_animation_builder_can_build_single_frame_animation() {
let frames =
DivoomAnimationResourceLoader::gif("test_data/animation_builder_tests/logo-16-0.gif")
.unwrap();
let frames = DivoomAnimationResourceLoader::gif_file(
"test_data/animation_builder_tests/logo-16-0.gif",
)
.unwrap();
assert_eq!(frames.len(), 1);

let builder = DivoomAnimationBuilder::new(16, Duration::from_millis(100)).unwrap();
Expand All @@ -229,9 +231,10 @@ mod tests {

#[test]
fn divoom_animation_builder_can_build_animation_with_fit() {
let frames =
DivoomAnimationResourceLoader::gif("test_data/animation_builder_tests/logo-16-0.gif")
.unwrap();
let frames = DivoomAnimationResourceLoader::gif_file(
"test_data/animation_builder_tests/logo-16-0.gif",
)
.unwrap();
assert_eq!(frames.len(), 1);

let builder = DivoomAnimationBuilder::new(32, Duration::from_millis(100)).unwrap();
Expand Down Expand Up @@ -278,9 +281,10 @@ mod tests {

#[test]
fn divoom_animation_builder_can_build_animation_with_rotation() {
let frames =
DivoomAnimationResourceLoader::gif("test_data/animation_builder_tests/logo-16-0.gif")
.unwrap();
let frames = DivoomAnimationResourceLoader::gif_file(
"test_data/animation_builder_tests/logo-16-0.gif",
)
.unwrap();
assert_eq!(frames.len(), 1);

let builder = DivoomAnimationBuilder::new(32, Duration::from_millis(100)).unwrap();
Expand All @@ -303,9 +307,10 @@ mod tests {

#[test]
fn divoom_animation_builder_can_build_animation_with_opacity() {
let frames =
DivoomAnimationResourceLoader::gif("test_data/animation_builder_tests/logo-16-0.gif")
.unwrap();
let frames = DivoomAnimationResourceLoader::gif_file(
"test_data/animation_builder_tests/logo-16-0.gif",
)
.unwrap();
assert_eq!(frames.len(), 1);

let builder = DivoomAnimationBuilder::new(32, Duration::from_millis(100)).unwrap();
Expand All @@ -328,9 +333,10 @@ mod tests {

#[test]
fn divoom_animation_builder_can_build_animation_with_sized() {
let frames =
DivoomAnimationResourceLoader::gif("test_data/animation_builder_tests/logo-16-0.gif")
.unwrap();
let frames = DivoomAnimationResourceLoader::gif_file(
"test_data/animation_builder_tests/logo-16-0.gif",
)
.unwrap();
assert_eq!(frames.len(), 1);

let builder = DivoomAnimationBuilder::new(32, Duration::from_millis(100)).unwrap();
Expand All @@ -346,9 +352,10 @@ mod tests {

#[test]
fn divoom_animation_builder_can_build_animation_with_scaled() {
let frames =
DivoomAnimationResourceLoader::gif("test_data/animation_builder_tests/logo-16-0.gif")
.unwrap();
let frames = DivoomAnimationResourceLoader::gif_file(
"test_data/animation_builder_tests/logo-16-0.gif",
)
.unwrap();
assert_eq!(frames.len(), 1);

let builder = DivoomAnimationBuilder::new(32, Duration::from_millis(100)).unwrap();
Expand All @@ -364,7 +371,7 @@ mod tests {

#[test]
fn divoom_animation_builder_can_build_multi_frame_animation() {
let frames = DivoomAnimationResourceLoader::gif(
let frames = DivoomAnimationResourceLoader::gif_file(
"test_data/animation_builder_tests/logo-16-rotate-4-frames.gif",
)
.unwrap();
Expand Down
66 changes: 47 additions & 19 deletions divoom/src/animation/animation_resource_loader.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,43 @@
use crate::{DivoomAPIError, DivoomAPIResult};
use std::fs::File;
use std::io::BufReader;
use std::io::{BufReader, Read};
use tiny_skia::Pixmap;

/// Load resources into a series of `tiny_skia::Pixmap`, so we can use them to build the animations.
pub struct DivoomAnimationResourceLoader {}

impl DivoomAnimationResourceLoader {
/// Load from local png file
pub fn png(file_path: &str) -> DivoomAPIResult<Pixmap> {
/// Load png resource from local file
pub fn png_file(file_path: &str) -> DivoomAPIResult<Pixmap> {
let frame = Pixmap::load_png(file_path)?;
Ok(frame)
}

/// Load from jpeg file
/// Load png resource from a memory buffer
pub fn png_buf(buf: &[u8]) -> DivoomAPIResult<Pixmap> {
let frame = Pixmap::decode_png(buf)?;
Ok(frame)
}

/// Load png resource from Read trait
pub fn png<R: Read>(reader: R) -> DivoomAPIResult<Pixmap> {
let mut buffer = Vec::new();
let mut buf_reader = BufReader::new(reader);
buf_reader.read_to_end(&mut buffer)?;
DivoomAnimationResourceLoader::png_buf(&buffer)
}

/// Load jpeg resource from local file
#[cfg(feature = "resource-loader-jpeg")]
pub fn jpeg(file_path: &str) -> DivoomAPIResult<Pixmap> {
pub fn jpeg_file(file_path: &str) -> DivoomAPIResult<Pixmap> {
let file = File::open(file_path)?;
let mut decoder = jpeg_decoder::Decoder::new(BufReader::new(file));
DivoomAnimationResourceLoader::jpeg(file)
}

/// Load jpeg resource from Read trait
#[cfg(feature = "resource-loader-jpeg")]
pub fn jpeg<R: Read>(reader: R) -> DivoomAPIResult<Pixmap> {
let mut decoder = jpeg_decoder::Decoder::new(BufReader::new(reader));
let pixels = decoder.decode()?;

let metadata = decoder.info().unwrap();
Expand Down Expand Up @@ -80,16 +100,22 @@ impl DivoomAnimationResourceLoader {
Ok(frame)
}

/// Load from local gif file
/// Load gif resource from local file
#[cfg(feature = "resource-loader-gif")]
pub fn gif(file_path: &str) -> DivoomAPIResult<Vec<Pixmap>> {
let mut frames = vec![];
pub fn gif_file(file_path: &str) -> DivoomAPIResult<Vec<Pixmap>> {
let input = File::open(file_path)?;
DivoomAnimationResourceLoader::gif(input)
}

/// Load gif resource from Read trait
#[cfg(feature = "resource-loader-gif")]
pub fn gif<R: Read>(reader: R) -> DivoomAPIResult<Vec<Pixmap>> {
let mut frames = vec![];

let mut options = gif::DecodeOptions::new();
options.set_color_output(gif::ColorOutput::RGBA);

let mut decoder = options.read_info(input)?;
let mut decoder = options.read_info(reader)?;
while let Some(frame) = decoder.read_next_frame()? {
let mut frame_pixmap = Pixmap::new(frame.width as u32, frame.height as u32).unwrap();
assert_eq!(frame_pixmap.data().len(), frame.buffer.len());
Expand Down Expand Up @@ -128,7 +154,7 @@ mod tests {
#[test]
fn divoom_resource_loader_can_load_png_file() {
let frame =
DivoomAnimationResourceLoader::png("test_data/animation_builder_tests/logo.png")
DivoomAnimationResourceLoader::png_file("test_data/animation_builder_tests/logo.png")
.unwrap();

let non_zero_bits_count = frame.data().as_ref().iter().filter(|x| **x != 0u8).count();
Expand All @@ -137,7 +163,7 @@ mod tests {

#[test]
fn divoom_resource_loader_can_load_jpeg_grayscale_file() {
let frame = DivoomAnimationResourceLoader::jpeg(
let frame = DivoomAnimationResourceLoader::jpeg_file(
"test_data/animation_builder_tests/logo_grayscale.jpg",
)
.unwrap();
Expand All @@ -148,19 +174,21 @@ mod tests {

#[test]
fn divoom_resource_loader_can_load_jpeg_rgb_file() {
let frame =
DivoomAnimationResourceLoader::jpeg("test_data/animation_builder_tests/logo_rgb.jpg")
.unwrap();
let frame = DivoomAnimationResourceLoader::jpeg_file(
"test_data/animation_builder_tests/logo_rgb.jpg",
)
.unwrap();

let non_zero_bits_count = frame.data().as_ref().iter().filter(|x| **x != 0u8).count();
assert_ne!(non_zero_bits_count, 0);
}

#[test]
fn divoom_resource_loader_can_load_jpeg_cmyk_file() {
let frame =
DivoomAnimationResourceLoader::jpeg("test_data/animation_builder_tests/logo_cmyk.jpg")
.unwrap();
let frame = DivoomAnimationResourceLoader::jpeg_file(
"test_data/animation_builder_tests/logo_cmyk.jpg",
)
.unwrap();

let non_zero_bits_count = frame.data().as_ref().iter().filter(|x| **x != 0u8).count();
assert_ne!(non_zero_bits_count, 0);
Expand All @@ -169,7 +197,7 @@ mod tests {
#[test]
fn divoom_resource_loader_can_load_gif_file() {
let frames =
DivoomAnimationResourceLoader::gif("test_data/animation_builder_tests/logo.gif")
DivoomAnimationResourceLoader::gif_file("test_data/animation_builder_tests/logo.gif")
.unwrap();
assert_eq!(frames.len(), 1);

Expand Down
4 changes: 2 additions & 2 deletions divoom/src/clients/pixoo/pixoo_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,7 @@ impl PixooClient {
file_path: &str,
) -> DivoomAPIResult<()> {
let animation_builder = DivoomAnimationBuilder::new(canvas_size, speed)?;
let gif = DivoomAnimationResourceLoader::gif(file_path)?;
let gif = DivoomAnimationResourceLoader::gif_file(file_path)?;
let animation = animation_builder
.draw_frames_fit(
&gif,
Expand Down Expand Up @@ -361,7 +361,7 @@ impl PixooClient {
blend: BlendMode,
) -> DivoomAPIResult<()> {
let animation_builder = DivoomAnimationBuilder::new(canvas_size, speed)?;
let gif = DivoomAnimationResourceLoader::gif(file_path)?;
let gif = DivoomAnimationResourceLoader::gif_file(file_path)?;
let animation = animation_builder
.draw_frames_fit(&gif, 0, fit, rotation, opacity, blend)
.build();
Expand Down
4 changes: 1 addition & 3 deletions divoom_cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -303,9 +303,7 @@ fn new_pixoo_client(common: &DivoomCliDeviceCommandCommonOpts) -> PixooClient {
.device_address
.as_ref()
.expect("Device Address is not set!"),
common
.timeout
.map_or(None, |x| Some(Duration::from_millis(x))),
common.timeout.map(|x| Duration::from_millis(x)),
)
}

Expand Down

0 comments on commit 0968fd2

Please sign in to comment.