From d4709b94047f7130c76834cc4391a7dc709efc56 Mon Sep 17 00:00:00 2001 From: Ivan Li Date: Sat, 19 Nov 2022 23:06:09 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E7=94=9F=E6=88=90=20LED=20=E7=81=AF?= =?UTF-8?q?=E6=9D=A1=E9=A2=9C=E8=89=B2=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src-tauri/Cargo.lock | 7 ++ src-tauri/Cargo.toml | 1 + src-tauri/src/led_color.rs | 50 +++++++++++ src-tauri/src/main.rs | 40 +++++++-- src-tauri/src/screen.rs | 124 +++++++++++++++++++++++++++ src-tauri/src/screen_color_picker.rs | 53 +++--------- src/App.tsx | 45 ++++++---- 7 files changed, 258 insertions(+), 62 deletions(-) create mode 100644 src-tauri/src/led_color.rs create mode 100644 src-tauri/src/screen.rs diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 552d2c2..20bef9a 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -1055,6 +1055,12 @@ dependencies = [ "libc", ] +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + [[package]] name = "html5ever" version = "0.25.2" @@ -2300,6 +2306,7 @@ dependencies = [ "anyhow", "base64", "bmp", + "hex", "once_cell", "paris", "scrap", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index caf5be7..64fd6fb 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -27,6 +27,7 @@ paris = { version = "1.5", features = ["timestamps", "macros"] } tokio = { version = "1.22.0", features = ["full"] } tracing = "0.1.37" tracing-subscriber = "0.3.16" +hex = "0.4.3" [features] # by default Tauri runs in production mode diff --git a/src-tauri/src/led_color.rs b/src-tauri/src/led_color.rs new file mode 100644 index 0000000..5a4b280 --- /dev/null +++ b/src-tauri/src/led_color.rs @@ -0,0 +1,50 @@ +use paris::info; +use serde::Serialize; + +#[derive(Clone, Copy)] +pub struct LedColor { + bits: [u8; 3], +} + +impl LedColor { + pub fn default() -> Self { + Self { bits: [0, 0, 0] } + } + + pub fn new(r: u8, g: u8, b: u8) -> Self { + Self { bits: [r, g, b] } + } + + pub fn get_rgb(&self) -> [u8; 3] { + self.bits + } + + pub fn is_empty(&self) -> bool { + self.bits.iter().any(|bit| *bit == 0) + } + + pub fn set_rgb(&mut self, r: u8, g: u8, b: u8) -> &Self { + self.bits = [r, g, b]; + self + } + + pub fn merge(&mut self, r: u8, g: u8, b: u8) -> &Self { + self.bits = [ + (self.bits[0] / 2 + r / 2), + (self.bits[1] / 2 + g / 2), + (self.bits[2] / 2 + b / 2), + ]; + self + } +} + +impl Serialize for LedColor { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + info!("{:?}", self.bits); + let hex = format!("#{}", hex::encode(self.bits)); + serializer.serialize_str(hex.as_str()) + } +} diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index bf7305d..15d03f7 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -3,8 +3,11 @@ windows_subsystem = "windows" )] +mod led_color; +mod screen; mod screen_color_picker; +use led_color::LedColor; use paris::*; use screen_color_picker::ScreenColorPicker; use std::{ @@ -30,11 +33,11 @@ async fn take_snapshot() -> Arc>> { Ok(bitmaps) => { info!("bitmaps len: {}", bitmaps.len()); match ScreenColorPicker::global().screens.lock() { - Ok(screens) => { - Some(Vec::from_iter(screens.iter().enumerate().map( - |(index, screen)| bitmap_to_webp_base64(screen.width, screen.height, bitmaps[index].to_vec()), - ))) - } + Ok(screens) => Some(Vec::from_iter(screens.iter().enumerate().map( + |(index, screen)| { + bitmap_to_webp_base64(screen.width, screen.height, bitmaps[index].to_vec()) + }, + ))), Err(error) => { error!("can not lock screens. {}", error); None @@ -65,6 +68,27 @@ async fn take_snapshot() -> Arc>> { } } +#[tauri::command] +async fn get_led_strip_colors() -> Result, String> { + let screens = ScreenColorPicker::global() + .screens + .lock() + .map_err(|error| { + error!("can not lock ScreenColorPick.screens. {}", error); + "failed to lock." + })?; + let mut colors = Vec::new(); + for screen in screens.iter() { + let result = screen.get_top_colors(); + if let Ok(result) = result { + colors.extend_from_slice(&result); + } else if let Err(result) = result { + return Err(format!("can not get led strip colors. {}", result)); + } + } + Ok(colors) +} + async fn bitmap_to_webp_base64(width: usize, height: usize, bitmap: Vec) -> String { let mut bitflipped = Vec::with_capacity(width * height * 3); let stride = bitmap.len() / height; @@ -89,7 +113,11 @@ async fn bitmap_to_webp_base64(width: usize, height: usize, bitmap: Vec) -> fn main() { tauri::Builder::default() - .invoke_handler(tauri::generate_handler![take_snapshot, refresh_displays]) + .invoke_handler(tauri::generate_handler![ + take_snapshot, + refresh_displays, + get_led_strip_colors + ]) .run(tauri::generate_context!()) .expect("error while running tauri application"); } diff --git a/src-tauri/src/screen.rs b/src-tauri/src/screen.rs new file mode 100644 index 0000000..1d8c024 --- /dev/null +++ b/src-tauri/src/screen.rs @@ -0,0 +1,124 @@ +use std::sync::{Arc, Mutex}; + +use anyhow::Ok; +use scrap::Capturer; + +use crate::led_color::LedColor; + +pub struct Screen { + bitmap: Arc>>>, + capturer: Option, + init_error: Option, + pub width: usize, + pub height: usize, + pub led_number_of_x: usize, + pub led_number_of_y: usize, +} + +impl Screen { + pub fn new(capturer: Capturer, width: usize, height: usize) -> Self { + Self { + bitmap: Arc::new(Mutex::new(None)), + capturer: Some(capturer), + init_error: None, + width, + height, + led_number_of_x: 0, + led_number_of_y: 0, + } + } + + pub fn new_failed(init_error: anyhow::Error, width: usize, height: usize) -> Self { + Self { + bitmap: Arc::new(Mutex::new(None)), + capturer: None, + init_error: Some(init_error), + width, + height, + led_number_of_x: 0, + led_number_of_y: 0, + } + } + + pub fn set_number_of_leds(&mut self, led_number_of_x: usize, led_number_of_y: usize) -> &Self { + self.led_number_of_x = led_number_of_x; + self.led_number_of_y = led_number_of_y; + self + } + + pub fn take(&mut self) -> anyhow::Result> { + match self.capturer.as_mut() { + Some(capturer) => { + let buffer = capturer + .frame() + .map_err(|error| anyhow::anyhow!("failed to frame of display. {}", error))?; + + self.bitmap = Arc::new(Mutex::new(Some(buffer.to_vec()))); + anyhow::Ok(buffer.to_vec()) + } + None => anyhow::bail!("Do not initialized"), + } + } + + pub fn get_top_colors(&self) -> anyhow::Result> { + self.get_x_colors(XPosition::Top) + } + pub fn get_bottom_colors(&self) -> anyhow::Result> { + self.get_x_colors(XPosition::Bottom) + } + + fn get_x_colors(&self, position: XPosition) -> anyhow::Result> { + if self.led_number_of_x == 0 { + return Ok(vec![]); + } + + let bitmap = self + .bitmap + .lock() + .map_err(|error| anyhow::anyhow!("can not lock Screen#bitmap. {}", error))?; + match bitmap.as_ref() { + Some(bitmap) => { + let cell_size_x = self.width / self.led_number_of_x; + let cell_size_y = self.height / 5; + let cell_size = cell_size_x * cell_size_y; + let y_range = match position { + XPosition::Top => 0..cell_size_y, + XPosition::Bottom => self.height - cell_size_y..self.height, + }; + + let mut colors = vec![LedColor::default(); self.led_number_of_x]; + let stride = bitmap.len() / self.height; + + for pos in 0..self.led_number_of_x { + let mut y_range = y_range.to_owned(); + + let mut r = 0u32; + let mut g = 0u32; + let mut b = 0u32; + for x in pos * cell_size_x..(pos + 1) * cell_size_x { + for y in y_range.to_owned() { + let i = stride * y + 4 * x; + r += bitmap[i + 2] as u32; + g += bitmap[i + 1] as u32; + b += bitmap[i] as u32; + } + } + colors[pos] = LedColor::new( + (r / cell_size as u32) as u8, + (g / cell_size as u32) as u8, + (b / cell_size as u32) as u8, + ); + } + return Ok(colors); + } + None => Ok(vec![]), + } + } +} + +unsafe impl Send for Screen {} + +enum XPosition { + Top, + Bottom, +} diff --git a/src-tauri/src/screen_color_picker.rs b/src-tauri/src/screen_color_picker.rs index 9b066ee..f988623 100644 --- a/src-tauri/src/screen_color_picker.rs +++ b/src-tauri/src/screen_color_picker.rs @@ -1,21 +1,13 @@ use once_cell::sync::OnceCell; +use paris::*; use scrap::{Capturer, Display}; +use tracing::field::display; use std::{ - io, + borrow::BorrowMut, sync::{Arc, Mutex}, }; -use paris::*; - -pub struct Screen { - bitmap: Option>, - capturer: Option, - init_error: Option, - pub height: usize, - pub width: usize, -} - -unsafe impl Send for Screen {} +use crate::screen::Screen; pub struct ScreenColorPicker { pub screens: Arc>>, @@ -43,22 +35,19 @@ impl ScreenColorPicker { let height = display.height(); let width = display.width(); match Capturer::new(display) { - Ok(capturer) => screens.push(Screen { - bitmap: None, - capturer: Some(capturer), - init_error: None, - height, + Ok(capturer) => screens.push(Screen::new(capturer, width, height)), + Err(error) => screens.push(Screen::new_failed( + anyhow::anyhow!("{}", error), width, - }), - Err(error) => screens.push(Screen { - bitmap: None, - capturer: None, - init_error: Some(error), height, - width, - }), + )), } } + + screens.reverse(); + screens[0].set_number_of_leds(22, 0); + screens[1].set_number_of_leds(38, 0); + Ok(()) } @@ -78,19 +67,3 @@ impl ScreenColorPicker { anyhow::Ok(screenshots) } } - -impl Screen { - fn take(&mut self) -> anyhow::Result> { - match self.capturer.as_mut() { - Some(capturer) => { - let buffer = capturer - .frame() - .map_err(|error| anyhow::anyhow!("failed to frame of display. {}", error))?; - - self.bitmap = Some(buffer.to_vec()); - anyhow::Ok(buffer.to_vec()) - } - None => anyhow::bail!("Do not initialized"), - } - } -} diff --git a/src/App.tsx b/src/App.tsx index d6b48b3..5684869 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -7,6 +7,7 @@ function App() { const [greetMsg, setGreetMsg] = useState(''); const [name, setName] = useState(''); const [screenshots, setScreenshots] = useState([]); + const [ledStripColors, setLedStripColors] = useState([]); async function greet() { // Learn more about Tauri commands at https://tauri.app/v1/guides/features/command @@ -23,20 +24,16 @@ function App() { await invoke('refresh_displays'); }, []); - return ( -
-

Welcome to Tauri!

+ const getLedStripColors = useCallback(async () => { + setLedStripColors(await invoke('get_led_strip_colors')); + }, []); -
- - Vite logo - - - Tauri logo - - - React logo - + return ( +
+
+ {ledStripColors.map((it) => ( + + ))}
@@ -47,8 +44,6 @@ function App() { ))}
-

Click on the Tauri, Vite, and React logos to learn more.

-
+ +
+ + -

{greetMsg}

); }