Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 2 additions & 43 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 6 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,13 @@ env_logger = { version = "^0.11.11", default-features = false, features = ["auto
# by slint 1.13.1
fontdue = "=0.9.3"
futures-util = "^0.3.33"
gif = "^0.14.2"
glam = { version = "^0.33.2", features = ["serde"] }
image = "=0.25.9"
image = { version = "=0.25.9", default-features = false, features = [
"png",
"gif",
"jpeg",
"webp",
] }
inotify = "^0.11.4"
ksni = { version = "^0.3.6", default-features = false, features = ["async-io"] }
libc = "^0.2.189"
Expand All @@ -57,9 +61,6 @@ mio = "^1.2.2"
# z = ["zbus", "serde", "async"]
notify-rust = { version = "=4.17.0", default-features = false, features = ["z"] }
nvml-wrapper = "^0.12.1"
pix = "=0.13.4"
png = "^0.18.1"
png_pong = "=0.9.3"
ron = "^0.12.2"
rusb = "^0.9.4"
sdl2 = { version = "^0.38.0", default-features = false }
Expand Down
2 changes: 1 addition & 1 deletion asusctl/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ argh.workspace = true

[dev-dependencies]
rog_dbus = { path = "../rog-dbus" }
png.workspace = true
image.workspace = true

[package.metadata.deb]
license-file = ["../LICENSE", "4"]
Expand Down
16 changes: 4 additions & 12 deletions asusctl/examples/anime-test-patterns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,31 +11,23 @@
//! Push each pattern to the matrix with:
//! asusctl anime pixel-image --path /tmp/g635l-patterns/<pattern>.png

use std::fs::{self, File};
use std::io::BufWriter;
use std::path::PathBuf;

use png::{BitDepth, ColorType, Encoder};
use std::fs;
use std::path::{Path, PathBuf};

const WIDTH: u32 = 68;
const HEIGHT: u32 = 34;
const OUT_DIR: &str = "/tmp/g635l-patterns";

/// Writes an 8-bit grayscale PNG. White = 0xff, black = 0x00.
fn write_png(path: &PathBuf, pixels: &[u8]) -> Result<(), Box<dyn std::error::Error>> {
fn write_png(path: &Path, pixels: &[u8]) -> Result<(), Box<dyn std::error::Error>> {
assert_eq!(
pixels.len(),
(WIDTH * HEIGHT) as usize,
"pixel buffer must be {} bytes",
WIDTH * HEIGHT
);

let file = File::create(path)?;
let mut encoder = Encoder::new(BufWriter::new(file), WIDTH, HEIGHT);
encoder.set_color(ColorType::Grayscale);
encoder.set_depth(BitDepth::Eight);
let mut writer = encoder.write_header()?;
writer.write_image_data(pixels)?;
image::save_buffer(path, pixels, WIDTH, HEIGHT, image::ExtendedColorType::L8)?;
Ok(())
}

Expand Down
4 changes: 1 addition & 3 deletions rog-anime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,7 @@ name = "rog_anime"
path = "src/lib.rs"

[dependencies]
png_pong.workspace = true
pix.workspace = true
gif.workspace = true
image.workspace = true
log.workspace = true

serde.workspace = true
Expand Down
4 changes: 2 additions & 2 deletions rog-anime/src/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::thread::sleep;
use std::time::{Duration, Instant};

use dmi_id::DMIID;
use log::info;
use log::{info, warn};
use serde::{Deserialize, Serialize};
#[cfg(feature = "dbus")]
use zbus::zvariant::{OwnedValue, Type, Value};
Expand Down Expand Up @@ -317,7 +317,7 @@ pub fn run_animation(frames: &AnimeGif, callback: &dyn Fn(AnimeDataBuffer) -> Re
fade_out_step = 1.0 / fade_out.as_secs_f32();

if time.total_fade_time() > run_time {
println!("Total fade in/out time larger than gif run time. Setting fades to half");
warn!("Total fade in/out time larger than gif run time. Setting fades to half");
fade_in = run_time / 2;
fade_in_step = 1.0 / (run_time / 2).as_secs_f32();

Expand Down
137 changes: 49 additions & 88 deletions rog-anime/src/diagonal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,19 @@ use log::error;
use crate::AnimeType;
use crate::data::AnimeDataBuffer;
use crate::error::{AnimeError, Result};
use crate::image::Pixel;

/// Mostly intended to be used with ASUS gifs, but can be used for other
/// purposes (like images)
#[derive(Clone, Debug)]
pub struct AnimeDiagonal(AnimeType, Vec<Vec<u8>>);

impl AnimeDiagonal {
/// Initialise a new matrix with 0's
#[inline]
pub fn new(anime_type: AnimeType) -> Self {
Self(
anime_type,
vec![vec![0; anime_type.width()]; anime_type.height()],
)
let matrix = vec![vec![0; anime_type.width()]; anime_type.height()];
Self(anime_type, matrix)
}

#[inline]
Expand All @@ -38,98 +39,33 @@ impl AnimeDiagonal {
buf
}

/// Generate the base image from inputs. The result can be displayed as is
/// or updated via scale, position, or angle then displayed again after
/// `update()`.
#[inline]
/// Make a diagonal matrix from a PNG file.
///
/// Accepts PNGs of dimensions up to the max for the laptop model.
/// For the G14, the max dimensions are 74x36. Returns [`AnimeError::IncorrectSize`]
/// if the image width or height exceeds the supported bounds.
pub fn from_png(path: &Path, bright: f32, anime_type: AnimeType) -> Result<Self> {
let data = std::fs::read(path).map_err(|e| {
let img = image::open(path).inspect_err(|e| {
error!("Could not open {path:?}: {e:?}");
e
})?;
let data = std::io::Cursor::new(data);
let decoder = png_pong::Decoder::new(data)?.into_steps();
let png_pong::Step { raster, delay: _ } = decoder.last().ok_or(AnimeError::NoFrames)??;
let rgba = img.to_rgba8();

let mut matrix = AnimeDiagonal::new(anime_type);

match &raster {
png_pong::PngRaster::Gray8(ras) => {
Self::pixels_from_8bit(ras, &mut matrix, bright, true);
}
png_pong::PngRaster::Graya8(ras) => {
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);
}
png_pong::PngRaster::Palette(..) => return Err(AnimeError::Format),
};
let max_w = anime_type.width() as u32;
let max_h = anime_type.height() as u32;
if rgba.width() > max_w || rgba.height() > max_h {
return Err(AnimeError::IncorrectSize(max_w, max_h));
}

Ok(matrix)
}
let mut matrix = AnimeDiagonal::new(anime_type);

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
};
if y < matrix.1.len() && x < matrix.1[y].len() {
matrix.1[y][x] = (v * bright) as u8;
}
}
for (x, y, px) in rgba.enumerate_pixels() {
let x = x as usize;
let y = y as usize;
let v = Pixel::from(px).color as f32;
matrix.1[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.1[y][x] = (v * bright) as u8;
}
}
Ok(matrix)
}

/// Convert to a data buffer that can be sent over dbus
Expand Down Expand Up @@ -457,3 +393,28 @@ impl AnimeDiagonal {
AnimeDataBuffer::from_vec(anime_type, buf)
}
}

#[cfg(test)]
mod tests {
use std::path::PathBuf;

use super::*;

#[test]
fn test_from_png_oversized_error() {
let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
path.push("tests/data/ga401-diagonal.png");

let res = AnimeDiagonal::from_png(&path, 1.0, AnimeType::G835L);
assert!(matches!(res, Err(AnimeError::IncorrectSize(68, 34))));
}

#[test]
fn test_from_png_valid() {
let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
path.push("tests/data/ga401-diagonal.png");

let res = AnimeDiagonal::from_png(&path, 1.0, AnimeType::GA401);
assert!(res.is_ok());
}
}
Loading