mirror of
https://gitlab.com/asus-linux/asusctl.git
synced 2026-02-06 00:15:04 +01:00
@@ -5,6 +5,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
### Added
|
||||||
|
- AniMe:
|
||||||
|
+ Support 8bit RGB, RGBA, 16bit Greyscalw, RGB, RGBA
|
||||||
|
+ add `AsusImage` type for slanted-template pixel-perfect images
|
||||||
|
+ `BREAKING:` plain `Image` with time period is changed and old anime configs break as a result (sorry)
|
||||||
|
|
||||||
# [3.7.2] - 2021-08-02
|
# [3.7.2] - 2021-08-02
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
12
MANUAL.md
12
MANUAL.md
@@ -236,6 +236,18 @@ Each object in the array can be one of:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
##### AsusImage
|
||||||
|
|
||||||
|
Virtually the same as `AsusAnimation` but for png files, typically created in the same "slanted" style using a template (`diagonal-template.png`) as the ASUS gifs for pixel perfection.
|
||||||
|
|
||||||
|
```json
|
||||||
|
"AsusImage": {
|
||||||
|
"file": "<FILE_PATH>",
|
||||||
|
"time": <TIME>,
|
||||||
|
"brightness": <FLOAT>
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
##### ImageAnimation
|
##### ImageAnimation
|
||||||
|
|
||||||
`ImageAnimation` can play *any* gif of any size.
|
`ImageAnimation` can play *any* gif of any size.
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ pub struct AnimeCommand {
|
|||||||
pub enum AnimeActions {
|
pub enum AnimeActions {
|
||||||
#[options(help = "change all leds brightness")]
|
#[options(help = "change all leds brightness")]
|
||||||
Leds(AnimeLeds),
|
Leds(AnimeLeds),
|
||||||
#[options(help = "display an 8bit greyscale png")]
|
#[options(help = "display an image png")]
|
||||||
Image(AnimeImage),
|
Image(AnimeImage),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,8 @@ use rog_profiles::profiles::Profile;
|
|||||||
use rog_types::{
|
use rog_types::{
|
||||||
gfx_vendors::{GfxRequiredUserAction, GfxVendors},
|
gfx_vendors::{GfxRequiredUserAction, GfxVendors},
|
||||||
supported::{
|
supported::{
|
||||||
FanCpuSupportedFunctions, LedSupportedFunctions, RogBiosSupportedFunctions,
|
AnimeSupportedFunctions, FanCpuSupportedFunctions, LedSupportedFunctions,
|
||||||
|
RogBiosSupportedFunctions,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use std::{env::args, path::Path};
|
use std::{env::args, path::Path};
|
||||||
@@ -149,54 +150,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
Some(CliCommand::LedMode(mode)) => handle_led_mode(&dbus, &supported.keyboard_led, &mode)?,
|
Some(CliCommand::LedMode(mode)) => handle_led_mode(&dbus, &supported.keyboard_led, &mode)?,
|
||||||
Some(CliCommand::Profile(cmd)) => handle_profile(&dbus, &supported.fan_cpu_ctrl, &cmd)?,
|
Some(CliCommand::Profile(cmd)) => handle_profile(&dbus, &supported.fan_cpu_ctrl, &cmd)?,
|
||||||
Some(CliCommand::Graphics(cmd)) => do_gfx(&dbus, &supported.rog_bios_ctrl, cmd)?,
|
Some(CliCommand::Graphics(cmd)) => do_gfx(&dbus, &supported.rog_bios_ctrl, cmd)?,
|
||||||
Some(CliCommand::Anime(cmd)) => {
|
Some(CliCommand::Anime(cmd)) => handle_anime(&dbus, &supported.anime_ctrl, &cmd)?,
|
||||||
if (cmd.command.is_none() && cmd.boot.is_none() && cmd.turn.is_none()) || cmd.help {
|
|
||||||
println!("Missing arg or command\n\n{}", cmd.self_usage());
|
|
||||||
if let Some(lst) = cmd.self_command_list() {
|
|
||||||
println!("\n{}", lst);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Some(anime_turn) = cmd.turn {
|
|
||||||
dbus.proxies().anime().set_led_power(anime_turn.into())?
|
|
||||||
}
|
|
||||||
if let Some(anime_boot) = cmd.boot {
|
|
||||||
dbus.proxies()
|
|
||||||
.anime()
|
|
||||||
.set_system_animations(anime_boot.into())?
|
|
||||||
}
|
|
||||||
if let Some(action) = cmd.command {
|
|
||||||
match action {
|
|
||||||
AnimeActions::Leds(anime_leds) => {
|
|
||||||
let data = AnimeDataBuffer::from_vec(
|
|
||||||
[anime_leds.led_brightness(); ANIME_DATA_LEN].to_vec(),
|
|
||||||
);
|
|
||||||
dbus.proxies().anime().write(data)?;
|
|
||||||
}
|
|
||||||
AnimeActions::Image(image) => {
|
|
||||||
if image.help_requested() {
|
|
||||||
println!("Missing arg or command\n\n{}", image.self_usage());
|
|
||||||
if let Some(lst) = image.self_command_list() {
|
|
||||||
println!("\n{}", lst);
|
|
||||||
}
|
|
||||||
std::process::exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
let matrix = AnimeImage::from_png(
|
|
||||||
Path::new(&image.path),
|
|
||||||
image.scale,
|
|
||||||
image.angle,
|
|
||||||
Vec2::new(image.x_pos, image.y_pos),
|
|
||||||
image.bright,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
dbus.proxies()
|
|
||||||
.anime()
|
|
||||||
.write(<AnimeDataBuffer>::from(&matrix))
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some(CliCommand::Bios(cmd)) => handle_bios_option(&dbus, &supported.rog_bios_ctrl, &cmd)?,
|
Some(CliCommand::Bios(cmd)) => handle_bios_option(&dbus, &supported.rog_bios_ctrl, &cmd)?,
|
||||||
None => {
|
None => {
|
||||||
if (!parsed.show_supported && parsed.kbd_bright.is_none() && parsed.chg_limit.is_none())
|
if (!parsed.show_supported && parsed.kbd_bright.is_none() && parsed.chg_limit.is_none())
|
||||||
@@ -292,6 +246,60 @@ fn do_gfx(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handle_anime(
|
||||||
|
dbus: &RogDbusClient,
|
||||||
|
_supported: &AnimeSupportedFunctions,
|
||||||
|
cmd: &AnimeCommand,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
if (cmd.command.is_none() && cmd.boot.is_none() && cmd.turn.is_none()) || cmd.help {
|
||||||
|
println!("Missing arg or command\n\n{}", cmd.self_usage());
|
||||||
|
if let Some(lst) = cmd.self_command_list() {
|
||||||
|
println!("\n{}", lst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(anime_turn) = cmd.turn {
|
||||||
|
dbus.proxies().anime().set_led_power(anime_turn.into())?
|
||||||
|
}
|
||||||
|
if let Some(anime_boot) = cmd.boot {
|
||||||
|
dbus.proxies()
|
||||||
|
.anime()
|
||||||
|
.set_system_animations(anime_boot.into())?
|
||||||
|
}
|
||||||
|
if let Some(action) = cmd.command.as_ref() {
|
||||||
|
match action {
|
||||||
|
AnimeActions::Leds(anime_leds) => {
|
||||||
|
let data = AnimeDataBuffer::from_vec(
|
||||||
|
[anime_leds.led_brightness(); ANIME_DATA_LEN].to_vec(),
|
||||||
|
);
|
||||||
|
dbus.proxies().anime().write(data)?;
|
||||||
|
}
|
||||||
|
AnimeActions::Image(image) => {
|
||||||
|
if image.help_requested() || image.path.is_empty() {
|
||||||
|
println!("Missing arg or command\n\n{}", image.self_usage());
|
||||||
|
if let Some(lst) = image.self_command_list() {
|
||||||
|
println!("\n{}", lst);
|
||||||
|
}
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let matrix = AnimeImage::from_png(
|
||||||
|
Path::new(&image.path),
|
||||||
|
image.scale,
|
||||||
|
image.angle,
|
||||||
|
Vec2::new(image.x_pos, image.y_pos),
|
||||||
|
image.bright,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
dbus.proxies()
|
||||||
|
.anime()
|
||||||
|
.write(<AnimeDataBuffer>::from(&matrix))
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn handle_led_mode(
|
fn handle_led_mode(
|
||||||
dbus: &RogDbusClient,
|
dbus: &RogDbusClient,
|
||||||
supported: &LedSupportedFunctions,
|
supported: &LedSupportedFunctions,
|
||||||
|
|||||||
@@ -261,12 +261,12 @@ impl CtrlAnime<'static> {
|
|||||||
scale: f32,
|
scale: f32,
|
||||||
angle: f32,
|
angle: f32,
|
||||||
xy: (f32, f32),
|
xy: (f32, f32),
|
||||||
time: Option<Timer>,
|
time: Timer,
|
||||||
brightness: f32,
|
brightness: f32,
|
||||||
) -> zbus::fdo::Result<String> {
|
) -> zbus::fdo::Result<String> {
|
||||||
if let Ok(mut config) = self.config.try_lock() {
|
if let Ok(mut config) = self.config.try_lock() {
|
||||||
let file = Path::new(&file);
|
let file = Path::new(&file);
|
||||||
let time = time.map(|time| time.into());
|
let time = time.into();
|
||||||
let action = ActionLoader::Image {
|
let action = ActionLoader::Image {
|
||||||
file: file.into(),
|
file: file.into(),
|
||||||
scale,
|
scale,
|
||||||
|
|||||||
@@ -95,6 +95,15 @@ impl Default for UserAnimeConfig {
|
|||||||
Self {
|
Self {
|
||||||
name: "default".to_string(),
|
name: "default".to_string(),
|
||||||
anime: vec![
|
anime: vec![
|
||||||
|
ActionLoader::AsusImage {
|
||||||
|
file: "/usr/share/asusd/anime/custom/diagonal-template.png".into(),
|
||||||
|
brightness: 1.0,
|
||||||
|
time: AnimTime::Fade(Fade::new(
|
||||||
|
Duration::from_secs(2),
|
||||||
|
None,
|
||||||
|
Duration::from_secs(2),
|
||||||
|
)),
|
||||||
|
},
|
||||||
ActionLoader::AsusAnimation {
|
ActionLoader::AsusAnimation {
|
||||||
file: "/usr/share/asusd/anime/asus/rog/Sunset.gif".into(),
|
file: "/usr/share/asusd/anime/asus/rog/Sunset.gif".into(),
|
||||||
brightness: 0.5,
|
brightness: 0.5,
|
||||||
@@ -121,11 +130,11 @@ impl Default for UserAnimeConfig {
|
|||||||
scale: 1.0,
|
scale: 1.0,
|
||||||
angle: 0.0,
|
angle: 0.0,
|
||||||
translation: Vec2::default(),
|
translation: Vec2::default(),
|
||||||
time: Some(AnimTime::Fade(Fade::new(
|
time: AnimTime::Fade(Fade::new(
|
||||||
Duration::from_secs(2),
|
Duration::from_secs(2),
|
||||||
Some(Duration::from_secs(1)),
|
Some(Duration::from_secs(1)),
|
||||||
Duration::from_secs(2),
|
Duration::from_secs(2),
|
||||||
))),
|
)),
|
||||||
brightness: 0.6,
|
brightness: 0.6,
|
||||||
},
|
},
|
||||||
ActionLoader::Pause(Duration::from_secs(1)),
|
ActionLoader::Pause(Duration::from_secs(1)),
|
||||||
|
|||||||
@@ -102,11 +102,12 @@ pub fn run_animation(
|
|||||||
|
|
||||||
let mut timed = false;
|
let mut timed = false;
|
||||||
let mut run_time = frames.total_frame_time();
|
let mut run_time = frames.total_frame_time();
|
||||||
println!("Real gif run length = {:?}", run_time);
|
|
||||||
if let AnimTime::Fade(time) = frames.duration() {
|
if let AnimTime::Fade(time) = frames.duration() {
|
||||||
if let Some(middle) = time.show_for() {
|
if let Some(middle) = time.show_for() {
|
||||||
run_time = middle + time.total_fade_time();
|
run_time = middle + time.total_fade_time();
|
||||||
}
|
}
|
||||||
|
// add a small buffer
|
||||||
|
run_time += Duration::from_millis(250);
|
||||||
timed = true;
|
timed = true;
|
||||||
} else if let AnimTime::Time(time) = frames.duration() {
|
} else if let AnimTime::Time(time) = frames.duration() {
|
||||||
run_time = time;
|
run_time = time;
|
||||||
|
|||||||
@@ -48,7 +48,6 @@ impl AnimeDiagonal {
|
|||||||
duration: Option<Duration>,
|
duration: Option<Duration>,
|
||||||
bright: f32,
|
bright: f32,
|
||||||
) -> Result<Self, AnimeError> {
|
) -> Result<Self, AnimeError> {
|
||||||
use pix::el::Pixel;
|
|
||||||
let data = std::fs::read(path)?;
|
let data = std::fs::read(path)?;
|
||||||
let data = std::io::Cursor::new(data);
|
let data = std::io::Cursor::new(data);
|
||||||
let decoder = png_pong::Decoder::new(data)?.into_steps();
|
let decoder = png_pong::Decoder::new(data)?.into_steps();
|
||||||
@@ -56,22 +55,78 @@ impl AnimeDiagonal {
|
|||||||
|
|
||||||
let mut matrix = AnimeDiagonal::new(duration);
|
let mut matrix = AnimeDiagonal::new(duration);
|
||||||
|
|
||||||
let width;
|
|
||||||
match raster {
|
match raster {
|
||||||
|
png_pong::PngRaster::Gray8(ras) => {
|
||||||
|
Self::pixels_from_8bit(ras, &mut matrix, bright, true)
|
||||||
|
}
|
||||||
png_pong::PngRaster::Graya8(ras) => {
|
png_pong::PngRaster::Graya8(ras) => {
|
||||||
width = ras.width();
|
Self::pixels_from_8bit(ras, &mut matrix, bright, true)
|
||||||
for (y, row) in ras.pixels().chunks(width as usize).enumerate() {
|
}
|
||||||
for (x, px) in row.iter().enumerate() {
|
png_pong::PngRaster::Rgb8(ras) => {
|
||||||
let v = <u8>::from(px.one() * bright);
|
Self::pixels_from_8bit(ras, &mut matrix, bright, false)
|
||||||
matrix.0[y][x] = v;
|
}
|
||||||
}
|
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),
|
_ => return Err(AnimeError::Format),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(matrix)
|
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 {
|
impl From<&AnimeDiagonal> for AnimeDataBuffer {
|
||||||
|
|||||||
@@ -128,6 +128,34 @@ impl AnimeGif {
|
|||||||
Ok(Self(frames, duration))
|
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
|
/// Create an animation using a gif of any size. This method must precompute the
|
||||||
/// result.
|
/// result.
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|||||||
@@ -249,7 +249,6 @@ impl AnimeImage {
|
|||||||
translation: Vec2,
|
translation: Vec2,
|
||||||
bright: f32,
|
bright: f32,
|
||||||
) -> Result<Self, AnimeError> {
|
) -> Result<Self, AnimeError> {
|
||||||
use pix::el::Pixel;
|
|
||||||
let data = std::fs::read(path)?;
|
let data = std::fs::read(path)?;
|
||||||
let data = std::io::Cursor::new(data);
|
let data = std::io::Cursor::new(data);
|
||||||
let decoder = png_pong::Decoder::new(data)?.into_steps();
|
let decoder = png_pong::Decoder::new(data)?.into_steps();
|
||||||
@@ -257,15 +256,37 @@ impl AnimeImage {
|
|||||||
|
|
||||||
let width;
|
let width;
|
||||||
let pixels = match raster {
|
let pixels = match raster {
|
||||||
|
png_pong::PngRaster::Gray8(ras) => {
|
||||||
|
width = ras.width();
|
||||||
|
Self::pixels_from_8bit(ras, true)
|
||||||
|
}
|
||||||
png_pong::PngRaster::Graya8(ras) => {
|
png_pong::PngRaster::Graya8(ras) => {
|
||||||
width = ras.width();
|
width = ras.width();
|
||||||
ras.pixels()
|
Self::pixels_from_8bit(ras, true)
|
||||||
.iter()
|
}
|
||||||
.map(|px| crate::image::Pixel {
|
png_pong::PngRaster::Rgb8(ras) => {
|
||||||
color: <u8>::from(px.one()) as u32,
|
width = ras.width();
|
||||||
alpha: <f32>::from(px.alpha()),
|
Self::pixels_from_8bit(ras, false)
|
||||||
})
|
}
|
||||||
.collect()
|
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),
|
_ => return Err(AnimeError::Format),
|
||||||
};
|
};
|
||||||
@@ -282,10 +303,48 @@ impl AnimeImage {
|
|||||||
matrix.update();
|
matrix.update();
|
||||||
Ok(matrix)
|
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 {
|
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
|
/// packets suitable for sending over USB
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(leds: &AnimeImage) -> Self {
|
fn from(leds: &AnimeImage) -> Self {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use std::{path::PathBuf, time::Duration};
|
|||||||
use glam::Vec2;
|
use glam::Vec2;
|
||||||
use serde_derive::{Deserialize, Serialize};
|
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
|
/// All the possible AniMe actions that can be used. This enum is intended to be
|
||||||
/// a helper for loading up `ActionData`.
|
/// a helper for loading up `ActionData`.
|
||||||
@@ -15,6 +15,12 @@ pub enum ActionLoader {
|
|||||||
time: AnimTime,
|
time: AnimTime,
|
||||||
brightness: f32,
|
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
|
/// Animated gif. If the file is a png a static gif is created using the `time` properties
|
||||||
ImageAnimation {
|
ImageAnimation {
|
||||||
file: PathBuf,
|
file: PathBuf,
|
||||||
@@ -29,7 +35,7 @@ pub enum ActionLoader {
|
|||||||
scale: f32,
|
scale: f32,
|
||||||
angle: f32,
|
angle: f32,
|
||||||
translation: Vec2,
|
translation: Vec2,
|
||||||
time: Option<AnimTime>,
|
time: AnimTime,
|
||||||
brightness: f32,
|
brightness: f32,
|
||||||
},
|
},
|
||||||
/// A pause to be used between sequences
|
/// A pause to be used between sequences
|
||||||
@@ -61,19 +67,29 @@ impl ActionData {
|
|||||||
let a = match action {
|
let a = match action {
|
||||||
ActionLoader::AsusAnimation {
|
ActionLoader::AsusAnimation {
|
||||||
file,
|
file,
|
||||||
time: duration,
|
time,
|
||||||
brightness,
|
brightness,
|
||||||
} => ActionData::Animation(AnimeGif::create_diagonal_gif(
|
} => ActionData::Animation(AnimeGif::create_diagonal_gif(file, *time, *brightness)?),
|
||||||
|
ActionLoader::AsusImage {
|
||||||
file,
|
file,
|
||||||
*duration,
|
time,
|
||||||
*brightness,
|
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 {
|
ActionLoader::ImageAnimation {
|
||||||
file,
|
file,
|
||||||
scale,
|
scale,
|
||||||
angle,
|
angle,
|
||||||
translation,
|
translation,
|
||||||
time: duration,
|
time,
|
||||||
brightness,
|
brightness,
|
||||||
} => {
|
} => {
|
||||||
if let Some(ext) = file.extension() {
|
if let Some(ext) = file.extension() {
|
||||||
@@ -83,7 +99,7 @@ impl ActionData {
|
|||||||
*scale,
|
*scale,
|
||||||
*angle,
|
*angle,
|
||||||
*translation,
|
*translation,
|
||||||
*duration,
|
*time,
|
||||||
*brightness,
|
*brightness,
|
||||||
)?));
|
)?));
|
||||||
}
|
}
|
||||||
@@ -93,7 +109,7 @@ impl ActionData {
|
|||||||
*scale,
|
*scale,
|
||||||
*angle,
|
*angle,
|
||||||
*translation,
|
*translation,
|
||||||
*duration,
|
*time,
|
||||||
*brightness,
|
*brightness,
|
||||||
)?)
|
)?)
|
||||||
}
|
}
|
||||||
@@ -105,20 +121,23 @@ impl ActionData {
|
|||||||
brightness,
|
brightness,
|
||||||
time,
|
time,
|
||||||
} => {
|
} => {
|
||||||
if let Some(time) = time {
|
match time {
|
||||||
return Ok(ActionData::Animation(AnimeGif::create_png_static(
|
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,
|
file,
|
||||||
*scale,
|
*scale,
|
||||||
*angle,
|
*angle,
|
||||||
*translation,
|
*translation,
|
||||||
*time,
|
*time,
|
||||||
*brightness,
|
*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),
|
ActionLoader::Pause(duration) => ActionData::Pause(*duration),
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user