mirror of
https://gitlab.com/asus-linux/asusctl.git
synced 2026-02-06 00:15:04 +01:00
@@ -102,11 +102,12 @@ pub fn run_animation(
|
||||
|
||||
let mut timed = false;
|
||||
let mut run_time = frames.total_frame_time();
|
||||
println!("Real gif run length = {:?}", run_time);
|
||||
if let AnimTime::Fade(time) = frames.duration() {
|
||||
if let Some(middle) = time.show_for() {
|
||||
run_time = middle + time.total_fade_time();
|
||||
}
|
||||
// add a small buffer
|
||||
run_time += Duration::from_millis(250);
|
||||
timed = true;
|
||||
} else if let AnimTime::Time(time) = frames.duration() {
|
||||
run_time = time;
|
||||
|
||||
@@ -48,7 +48,6 @@ impl AnimeDiagonal {
|
||||
duration: Option<Duration>,
|
||||
bright: f32,
|
||||
) -> Result<Self, AnimeError> {
|
||||
use pix::el::Pixel;
|
||||
let data = std::fs::read(path)?;
|
||||
let data = std::io::Cursor::new(data);
|
||||
let decoder = png_pong::Decoder::new(data)?.into_steps();
|
||||
@@ -56,22 +55,78 @@ impl AnimeDiagonal {
|
||||
|
||||
let mut matrix = AnimeDiagonal::new(duration);
|
||||
|
||||
let width;
|
||||
match raster {
|
||||
png_pong::PngRaster::Gray8(ras) => {
|
||||
Self::pixels_from_8bit(ras, &mut matrix, bright, true)
|
||||
}
|
||||
png_pong::PngRaster::Graya8(ras) => {
|
||||
width = ras.width();
|
||||
for (y, row) in ras.pixels().chunks(width as usize).enumerate() {
|
||||
for (x, px) in row.iter().enumerate() {
|
||||
let v = <u8>::from(px.one() * bright);
|
||||
matrix.0[y][x] = v;
|
||||
}
|
||||
}
|
||||
Self::pixels_from_8bit(ras, &mut matrix, bright, true)
|
||||
}
|
||||
png_pong::PngRaster::Rgb8(ras) => {
|
||||
Self::pixels_from_8bit(ras, &mut matrix, bright, false)
|
||||
}
|
||||
png_pong::PngRaster::Rgba8(ras) => {
|
||||
Self::pixels_from_8bit(ras, &mut matrix, bright, false)
|
||||
}
|
||||
png_pong::PngRaster::Gray16(ras) => {
|
||||
Self::pixels_from_16bit(ras, &mut matrix, bright, true)
|
||||
}
|
||||
png_pong::PngRaster::Rgb16(ras) => {
|
||||
Self::pixels_from_16bit(ras, &mut matrix, bright, false)
|
||||
}
|
||||
png_pong::PngRaster::Graya16(ras) => {
|
||||
Self::pixels_from_16bit(ras, &mut matrix, bright, true)
|
||||
}
|
||||
png_pong::PngRaster::Rgba16(ras) => {
|
||||
Self::pixels_from_16bit(ras, &mut matrix, bright, false)
|
||||
}
|
||||
_ => return Err(AnimeError::Format),
|
||||
};
|
||||
|
||||
Ok(matrix)
|
||||
}
|
||||
|
||||
fn pixels_from_8bit<P>(ras: pix::Raster<P>, matrix: &mut AnimeDiagonal, bright: f32, grey: bool)
|
||||
where
|
||||
P: pix::el::Pixel<Chan = pix::chan::Ch8>,
|
||||
{
|
||||
let width = ras.width();
|
||||
for (y, row) in ras.pixels().chunks(width as usize).enumerate() {
|
||||
for (x, px) in row.iter().enumerate() {
|
||||
let v = if grey {
|
||||
<u8>::from(px.one()) as f32
|
||||
} else {
|
||||
(<u8>::from(px.one()) / 3) as f32
|
||||
+ (<u8>::from(px.two()) / 3) as f32
|
||||
+ (<u8>::from(px.three()) / 3) as f32
|
||||
};
|
||||
matrix.0[y][x] = (v * bright) as u8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn pixels_from_16bit<P>(
|
||||
ras: pix::Raster<P>,
|
||||
matrix: &mut AnimeDiagonal,
|
||||
bright: f32,
|
||||
grey: bool,
|
||||
) where
|
||||
P: pix::el::Pixel<Chan = pix::chan::Ch16>,
|
||||
{
|
||||
let width = ras.width();
|
||||
for (y, row) in ras.pixels().chunks(width as usize).enumerate() {
|
||||
for (x, px) in row.iter().enumerate() {
|
||||
let v = if grey {
|
||||
(<u16>::from(px.one()) >> 8) as f32
|
||||
} else {
|
||||
((<u16>::from(px.one()) / 3) >> 8) as f32
|
||||
+ ((<u16>::from(px.two()) / 3) >> 8) as f32
|
||||
+ ((<u16>::from(px.three()) / 3) >> 8) as f32
|
||||
};
|
||||
matrix.0[y][x] = (v * bright) as u8;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&AnimeDiagonal> for AnimeDataBuffer {
|
||||
|
||||
@@ -128,6 +128,34 @@ impl AnimeGif {
|
||||
Ok(Self(frames, duration))
|
||||
}
|
||||
|
||||
/// Create an animation using the 74x36 ASUS gif format from a png
|
||||
#[inline]
|
||||
pub fn create_diagonal_png(
|
||||
file_name: &Path,
|
||||
duration: AnimTime,
|
||||
brightness: f32,
|
||||
) -> Result<Self, AnimeError> {
|
||||
let image = AnimeDiagonal::from_png(file_name, None, brightness)?;
|
||||
|
||||
let mut total = Duration::from_millis(1000);
|
||||
if let AnimTime::Fade(fade) = duration {
|
||||
total = fade.total_fade_time();
|
||||
if let Some(middle) = fade.show_for {
|
||||
total += middle;
|
||||
}
|
||||
}
|
||||
// Make frame delay 30ms, and find frame count
|
||||
let frame_count = total.as_millis() / 30;
|
||||
|
||||
let single = AnimeFrame {
|
||||
data: <AnimeDataBuffer>::from(&image),
|
||||
delay: Duration::from_millis(30),
|
||||
};
|
||||
let frames = vec![single; frame_count as usize];
|
||||
|
||||
Ok(Self(frames, duration))
|
||||
}
|
||||
|
||||
/// Create an animation using a gif of any size. This method must precompute the
|
||||
/// result.
|
||||
#[inline]
|
||||
|
||||
@@ -249,7 +249,6 @@ impl AnimeImage {
|
||||
translation: Vec2,
|
||||
bright: f32,
|
||||
) -> Result<Self, AnimeError> {
|
||||
use pix::el::Pixel;
|
||||
let data = std::fs::read(path)?;
|
||||
let data = std::io::Cursor::new(data);
|
||||
let decoder = png_pong::Decoder::new(data)?.into_steps();
|
||||
@@ -257,15 +256,37 @@ impl AnimeImage {
|
||||
|
||||
let width;
|
||||
let pixels = match raster {
|
||||
png_pong::PngRaster::Gray8(ras) => {
|
||||
width = ras.width();
|
||||
Self::pixels_from_8bit(ras, true)
|
||||
}
|
||||
png_pong::PngRaster::Graya8(ras) => {
|
||||
width = ras.width();
|
||||
ras.pixels()
|
||||
.iter()
|
||||
.map(|px| crate::image::Pixel {
|
||||
color: <u8>::from(px.one()) as u32,
|
||||
alpha: <f32>::from(px.alpha()),
|
||||
})
|
||||
.collect()
|
||||
Self::pixels_from_8bit(ras, true)
|
||||
}
|
||||
png_pong::PngRaster::Rgb8(ras) => {
|
||||
width = ras.width();
|
||||
Self::pixels_from_8bit(ras, false)
|
||||
}
|
||||
png_pong::PngRaster::Rgba8(ras) => {
|
||||
width = ras.width();
|
||||
Self::pixels_from_8bit(ras, false)
|
||||
}
|
||||
png_pong::PngRaster::Gray16(ras) => {
|
||||
width = ras.width();
|
||||
Self::pixels_from_16bit(ras, true)
|
||||
}
|
||||
png_pong::PngRaster::Rgb16(ras) => {
|
||||
width = ras.width();
|
||||
Self::pixels_from_16bit(ras, false)
|
||||
}
|
||||
png_pong::PngRaster::Graya16(ras) => {
|
||||
width = ras.width();
|
||||
Self::pixels_from_16bit(ras, true)
|
||||
}
|
||||
png_pong::PngRaster::Rgba16(ras) => {
|
||||
width = ras.width();
|
||||
Self::pixels_from_16bit(ras, false)
|
||||
}
|
||||
_ => return Err(AnimeError::Format),
|
||||
};
|
||||
@@ -282,10 +303,48 @@ impl AnimeImage {
|
||||
matrix.update();
|
||||
Ok(matrix)
|
||||
}
|
||||
|
||||
fn pixels_from_8bit<P>(ras: pix::Raster<P>, grey: bool) -> Vec<Pixel>
|
||||
where
|
||||
P: pix::el::Pixel<Chan = pix::chan::Ch8>,
|
||||
{
|
||||
ras.pixels()
|
||||
.iter()
|
||||
.map(|px| crate::image::Pixel {
|
||||
color: if grey {
|
||||
<u8>::from(px.one()) as u32
|
||||
} else {
|
||||
(<u8>::from(px.one()) / 3) as u32
|
||||
+ (<u8>::from(px.two()) / 3) as u32
|
||||
+ (<u8>::from(px.three()) / 3) as u32
|
||||
},
|
||||
alpha: <f32>::from(px.alpha()),
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn pixels_from_16bit<P>(ras: pix::Raster<P>, grey: bool) -> Vec<Pixel>
|
||||
where
|
||||
P: pix::el::Pixel<Chan = pix::chan::Ch16>,
|
||||
{
|
||||
ras.pixels()
|
||||
.iter()
|
||||
.map(|px| crate::image::Pixel {
|
||||
color: if grey {
|
||||
(<u16>::from(px.one()) >> 8) as u32
|
||||
} else {
|
||||
((<u16>::from(px.one()) / 3) >> 8) as u32
|
||||
+ ((<u16>::from(px.two()) / 3) >> 8) as u32
|
||||
+ ((<u16>::from(px.three()) / 3) >> 8) as u32
|
||||
},
|
||||
alpha: <f32>::from(px.alpha()),
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&AnimeImage> for AnimeDataBuffer {
|
||||
/// Do conversion from the nested Vec in AniMeMatrix to the two required
|
||||
/// Do conversion from the nested Vec in AnimeDataBuffer to the two required
|
||||
/// packets suitable for sending over USB
|
||||
#[inline]
|
||||
fn from(leds: &AnimeImage) -> Self {
|
||||
|
||||
@@ -3,7 +3,7 @@ use std::{path::PathBuf, time::Duration};
|
||||
use glam::Vec2;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
|
||||
use crate::{error::AnimeError, AnimTime, AnimeDataBuffer, AnimeGif, AnimeImage};
|
||||
use crate::{error::AnimeError, AnimTime, AnimeDataBuffer, AnimeDiagonal, AnimeGif, AnimeImage};
|
||||
|
||||
/// All the possible AniMe actions that can be used. This enum is intended to be
|
||||
/// a helper for loading up `ActionData`.
|
||||
@@ -15,6 +15,12 @@ pub enum ActionLoader {
|
||||
time: AnimTime,
|
||||
brightness: f32,
|
||||
},
|
||||
/// Image designed to be pixel perfect using the slanted template
|
||||
AsusImage {
|
||||
file: PathBuf,
|
||||
time: AnimTime,
|
||||
brightness: f32,
|
||||
},
|
||||
/// Animated gif. If the file is a png a static gif is created using the `time` properties
|
||||
ImageAnimation {
|
||||
file: PathBuf,
|
||||
@@ -29,7 +35,7 @@ pub enum ActionLoader {
|
||||
scale: f32,
|
||||
angle: f32,
|
||||
translation: Vec2,
|
||||
time: Option<AnimTime>,
|
||||
time: AnimTime,
|
||||
brightness: f32,
|
||||
},
|
||||
/// A pause to be used between sequences
|
||||
@@ -61,19 +67,29 @@ impl ActionData {
|
||||
let a = match action {
|
||||
ActionLoader::AsusAnimation {
|
||||
file,
|
||||
time: duration,
|
||||
time,
|
||||
brightness,
|
||||
} => ActionData::Animation(AnimeGif::create_diagonal_gif(
|
||||
} => ActionData::Animation(AnimeGif::create_diagonal_gif(file, *time, *brightness)?),
|
||||
ActionLoader::AsusImage {
|
||||
file,
|
||||
*duration,
|
||||
*brightness,
|
||||
)?),
|
||||
time,
|
||||
brightness,
|
||||
} => match time {
|
||||
AnimTime::Infinite => {
|
||||
let image = AnimeDiagonal::from_png(file, None, *brightness)?;
|
||||
let data = <AnimeDataBuffer>::from(&image);
|
||||
ActionData::Image(Box::new(data))
|
||||
}
|
||||
_ => {
|
||||
ActionData::Animation(AnimeGif::create_diagonal_png(file, *time, *brightness)?)
|
||||
}
|
||||
},
|
||||
ActionLoader::ImageAnimation {
|
||||
file,
|
||||
scale,
|
||||
angle,
|
||||
translation,
|
||||
time: duration,
|
||||
time,
|
||||
brightness,
|
||||
} => {
|
||||
if let Some(ext) = file.extension() {
|
||||
@@ -83,7 +99,7 @@ impl ActionData {
|
||||
*scale,
|
||||
*angle,
|
||||
*translation,
|
||||
*duration,
|
||||
*time,
|
||||
*brightness,
|
||||
)?));
|
||||
}
|
||||
@@ -93,7 +109,7 @@ impl ActionData {
|
||||
*scale,
|
||||
*angle,
|
||||
*translation,
|
||||
*duration,
|
||||
*time,
|
||||
*brightness,
|
||||
)?)
|
||||
}
|
||||
@@ -105,20 +121,23 @@ impl ActionData {
|
||||
brightness,
|
||||
time,
|
||||
} => {
|
||||
if let Some(time) = time {
|
||||
return Ok(ActionData::Animation(AnimeGif::create_png_static(
|
||||
match time {
|
||||
AnimTime::Infinite => {
|
||||
// If no time then create a plain static image
|
||||
let image =
|
||||
AnimeImage::from_png(file, *scale, *angle, *translation, *brightness)?;
|
||||
let data = <AnimeDataBuffer>::from(&image);
|
||||
ActionData::Image(Box::new(data))
|
||||
}
|
||||
_ => ActionData::Animation(AnimeGif::create_png_static(
|
||||
file,
|
||||
*scale,
|
||||
*angle,
|
||||
*translation,
|
||||
*time,
|
||||
*brightness,
|
||||
)?));
|
||||
)?),
|
||||
}
|
||||
// If no time then create a plain static image
|
||||
let image = AnimeImage::from_png(file, *scale, *angle, *translation, *brightness)?;
|
||||
let data = <AnimeDataBuffer>::from(&image);
|
||||
ActionData::Image(Box::new(data))
|
||||
}
|
||||
ActionLoader::Pause(duration) => ActionData::Pause(*duration),
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user