From 47c1ca9fe410062d3f020d12e0dc4d0cc2178b64 Mon Sep 17 00:00:00 2001 From: Luke D Jones Date: Tue, 6 Apr 2021 21:03:05 +1200 Subject: [PATCH] anime: gif-image --- asusctl/examples/animatrix-gif.rs | 2 +- asusctl/examples/animatrix-png-gif.rs | 29 +++++++++++++++++ rog-anime/src/anime_gif.rs | 46 +++++++++++++++++++++++++-- rog-anime/src/anime_image.rs | 12 ++++--- rog-anime/src/lib.rs | 11 ++++++- 5 files changed, 92 insertions(+), 8 deletions(-) create mode 100644 asusctl/examples/animatrix-png-gif.rs diff --git a/asusctl/examples/animatrix-gif.rs b/asusctl/examples/animatrix-gif.rs index e95d54f4..a5a48b2e 100644 --- a/asusctl/examples/animatrix-gif.rs +++ b/asusctl/examples/animatrix-gif.rs @@ -1,4 +1,4 @@ -use std::{env, fs::File, path::Path, thread::sleep, time::Duration}; +use std::{env, path::Path, thread::sleep}; use rog_anime::AniMeSequence; use rog_dbus::AuraDbusClient; diff --git a/asusctl/examples/animatrix-png-gif.rs b/asusctl/examples/animatrix-png-gif.rs new file mode 100644 index 00000000..12abc94a --- /dev/null +++ b/asusctl/examples/animatrix-png-gif.rs @@ -0,0 +1,29 @@ +use std::{env, path::Path, thread::sleep}; + +use rog_anime::AniMeSequence; +use rog_dbus::AuraDbusClient; + +fn main() { + let (client, _) = AuraDbusClient::new().unwrap(); + + let args: Vec = env::args().into_iter().collect(); + if args.len() != 3 { + println!("Please supply filepath and brightness"); + return; + } + + let path = Path::new(&args[1]); + let brightness = args[2].parse::().unwrap(); + let gif = AniMeSequence::png_gif(path, brightness).unwrap(); + + loop { + for frame in gif.get_animation().unwrap().frames() { + client + .proxies() + .anime() + .write(frame.frame().clone()) + .unwrap(); + sleep(frame.delay()); + } + } +} diff --git a/rog-anime/src/anime_gif.rs b/rog-anime/src/anime_gif.rs index 7ef5b2cf..5bc209b8 100644 --- a/rog-anime/src/anime_gif.rs +++ b/rog-anime/src/anime_gif.rs @@ -1,7 +1,8 @@ use serde_derive::{Deserialize, Serialize}; use std::{fs::File, path::Path, time::Duration}; +use glam::Vec2; -use crate::{error::AnimeError, AniMeDataBuffer, AniMeDiagonal}; +use crate::{AniMeDataBuffer, AniMeDiagonal, AniMeImage, error::AnimeError, Pixel}; #[derive(Debug, Clone, Deserialize, Serialize)] pub struct AniMeFrame { @@ -25,7 +26,7 @@ impl AniMeFrame { pub struct AniMeGif(Vec); impl AniMeGif { - pub fn new(file_name: &Path, brightness: f32) -> Result { + pub fn create_diagonal_gif(file_name: &Path, brightness: f32) -> Result { let mut frames = Vec::new(); let mut matrix = AniMeDiagonal::new(); @@ -57,6 +58,47 @@ impl AniMeGif { Ok(Self(frames)) } + pub fn create_png_gif(file_name: &Path, brightness: f32) -> Result { + let mut frames = Vec::new(); + + let mut decoder = gif::DecodeOptions::new(); + // Configure the decoder such that it will expand the image to RGBA. + decoder.set_color_output(gif::ColorOutput::RGBA); + // Read the file header + let file = File::open(file_name)?; + let mut decoder = decoder.read_info(file)?; + + let width = decoder.width(); + let pixels: Vec = vec![Pixel::default(); (decoder.width() as u32 * decoder.height() as u32) as usize]; + let mut image = AniMeImage::new(Vec2::new(1.0, 1.0), 0.0, Vec2::new(0.0, 0.0), + brightness, pixels, decoder.width() as u32); + + while let Some(frame) = decoder.read_next_frame()? { + let wait = frame.delay * 10; + for (y, row) in frame.buffer.chunks(frame.width as usize * 4).enumerate() { + for (x, px) in row.chunks(4).enumerate() { + if px[3] != 255 { + // should be t but not in some gifs? What, ASUS, what? + continue; + } + let pos = (x + frame.left as usize) + ((y + frame.top as usize) * width as usize); + image.get_mut()[pos] = + Pixel { + color: ((px[0] as u32 + px[1] as u32 + px[2] as u32) / 3), + alpha: 1.0, + }; + } + } + image.update(); + + frames.push(AniMeFrame { + data: ::from(&image), + delay: Duration::from_millis(wait as u64), + }); + } + Ok(Self(frames)) + } + pub fn frames(&self) -> &[AniMeFrame] { &self.0 } diff --git a/rog-anime/src/anime_image.rs b/rog-anime/src/anime_image.rs index d0972987..f9bcd18e 100644 --- a/rog-anime/src/anime_image.rs +++ b/rog-anime/src/anime_image.rs @@ -11,9 +11,9 @@ use crate::{ const LED_PIXEL_LEN: usize = 1244; #[derive(Copy, Clone, Debug, Default)] -struct Pixel { - color: u32, - alpha: f32, +pub(crate) struct Pixel { + pub color: u32, + pub alpha: f32, } /// A single LED position and brightness. The intention of this struct @@ -64,7 +64,7 @@ pub struct AniMeImage { } impl AniMeImage { - const fn new( + pub(crate) const fn new( scale: Vec2, angle: f32, translation: Vec2, @@ -131,6 +131,10 @@ impl AniMeImage { } } + pub(crate) fn get_mut(&mut self) -> &mut [Pixel] { + &mut self.img_pixels + } + /// Really only used to generate the output for including as a full const in `LED_IMAGE_POSITIONS` pub fn generate() -> Vec> { (0..AniMeImage::height()) diff --git a/rog-anime/src/lib.rs b/rog-anime/src/lib.rs index e92b0da1..cd62bad6 100644 --- a/rog-anime/src/lib.rs +++ b/rog-anime/src/lib.rs @@ -39,7 +39,7 @@ pub enum AniMeSequence { impl AniMeSequence { pub fn gif(file: &Path, brightness: f32) -> Result { - let frames = AniMeGif::new(file, brightness)?; + let frames = AniMeGif::create_diagonal_gif(file, brightness)?; Ok(Self::Animation(frames)) } @@ -55,6 +55,15 @@ impl AniMeSequence { Ok(Self::Image(Box::new(data))) } + pub fn png_gif(file: &Path, + // scale: Vec2, + // angle: f32, + // translation: Vec2, + brightness: f32,) -> Result { + let frames = AniMeGif::create_png_gif(file, brightness)?; + Ok(Self::Animation(frames)) + } + pub fn get_animation(&self) -> Option<&AniMeGif> { match self { AniMeSequence::Animation(anim) => Some(anim),