diff --git a/src-tauri/src/core/core.rs b/src-tauri/src/core/core.rs index b89074d..3495b9f 100644 --- a/src-tauri/src/core/core.rs +++ b/src-tauri/src/core/core.rs @@ -118,38 +118,40 @@ impl CoreManager { let mut global_colors = HashMap::new(); while let Some(screenshot) = rx.recv().await { let start_at = Instant::now(); - match screenshot.get_top_colors().await { - Ok(colors) => { - let start = screenshot.get_top_of_led_start_at().min(screenshot.get_top_of_led_end_at()); + let colors = screenshot.get_top_colors(); + let start = screenshot + .get_top_of_led_start_at() + .min(screenshot.get_top_of_led_end_at()); - let colors_len = colors.len(); - for (index, color) in colors.into_iter().enumerate() { - global_colors.insert(index + start, color); + let colors_len = colors.len(); + for (index, color) in colors.into_iter().enumerate() { + global_colors.insert(index + start, color); + } + + info!( + "led count: {}, spend: {:?}", + global_colors.len(), + start_at.elapsed() + ); + + if global_colors.len() == 60 { + let mut colors = vec![]; + for index in 0..global_colors.len() { + colors.push(*global_colors.get(&index).unwrap()); + } + global_colors = HashMap::new(); + match rpc::manager::Manager::global() + .publish_led_colors(&colors) + .await + { + Ok(_) => { + info!("publish successful",); } - - info!("led count: {}, spend: {:?}", global_colors.len(), start_at.elapsed()); - - if global_colors.len() == 60 { - let mut colors = vec![]; - for index in 0..global_colors.len() { - colors.push(*global_colors.get(&index).unwrap()); - } - global_colors = HashMap::new(); - match rpc::manager::Manager::global() - .publish_led_colors(&colors) - .await - { - Ok(_) => { - info!("publish successful",); - } - Err(error) => { - warn!("publish led colors failed. {}", error); - } - } + Err(error) => { + warn!("publish led colors failed. {}", error); } } - Err(_) => {} - }; + } } }); diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 94ca7c4..35a81c6 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -2,6 +2,7 @@ all(not(debug_assertions), target_os = "windows"), windows_subsystem = "windows" )] +#![feature(bool_to_option)] mod core; mod picker; @@ -11,10 +12,11 @@ use crate::core::AmbientLightMode; use crate::core::CoreManager; use paris::*; use picker::manager::Picker; +use picker::screenshot::ScreenshotDto; use std::vec; #[tauri::command] -async fn take_snapshot() -> Vec { +async fn take_snapshot() -> Vec { let manager = Picker::global(); let start = time::Instant::now(); @@ -29,7 +31,7 @@ async fn take_snapshot() -> Vec { vec![] } }; - info!("截图花费 {} s", start.elapsed().as_seconds_f32()); + info!("截图耗时 {} s", start.elapsed().as_seconds_f32()); base64_bitmap_list } diff --git a/src-tauri/src/picker/config/display_config.rs b/src-tauri/src/picker/config/display_config.rs index cd2c9eb..ec1be7a 100644 --- a/src-tauri/src/picker/config/display_config.rs +++ b/src-tauri/src/picker/config/display_config.rs @@ -12,10 +12,10 @@ pub struct DisplayConfig { pub index_of_display: usize, pub display_width: usize, pub display_height: usize, - pub top_led_strip: LedStripConfig, - pub bottom_led_strip: LedStripConfig, - pub left_led_strip: LedStripConfig, - pub right_led_strip: LedStripConfig, + pub top_led_strip: Option, + pub bottom_led_strip: Option, + pub left_led_strip: Option, + pub right_led_strip: Option, } impl DisplayConfig { @@ -24,26 +24,10 @@ impl DisplayConfig { index_of_display, display_width, display_height, - top_led_strip: LedStripConfig { - index: 0, - global_start_position: 0, - global_end_position: 0, - }, - bottom_led_strip: LedStripConfig { - index: 0, - global_start_position: 0, - global_end_position: 0, - }, - left_led_strip: LedStripConfig { - index: 0, - global_start_position: 0, - global_end_position: 0, - }, - right_led_strip: LedStripConfig { - index: 0, - global_start_position: 0, - global_end_position: 0, - }, + top_led_strip: None, + bottom_led_strip: None, + left_led_strip: None, + right_led_strip: None, } } } diff --git a/src-tauri/src/picker/led_color.rs b/src-tauri/src/picker/led_color.rs index 073c841..98c371c 100644 --- a/src-tauri/src/picker/led_color.rs +++ b/src-tauri/src/picker/led_color.rs @@ -1,7 +1,7 @@ use color_space::{Hsv, Rgb}; use serde::Serialize; -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug)] pub struct LedColor { bits: [u8; 3], } diff --git a/src-tauri/src/picker/manager.rs b/src-tauri/src/picker/manager.rs index 1300807..5963f6d 100644 --- a/src-tauri/src/picker/manager.rs +++ b/src-tauri/src/picker/manager.rs @@ -2,12 +2,16 @@ use futures::{stream::FuturesUnordered, StreamExt}; use once_cell::sync::OnceCell; use paris::info; use scrap::Display; -use std::{sync::Arc, borrow::Borrow}; +use std::{borrow::Borrow, sync::Arc}; use tokio::{sync::Mutex, task}; use crate::picker::{config, screen::Screen}; -use super::{config::DisplayConfig, display_picker::DisplayPicker, screenshot::Screenshot}; +use super::{ + config::DisplayConfig, + display_picker::DisplayPicker, + screenshot::{Screenshot, ScreenshotDto}, +}; pub struct Picker { pub screens: Arc>>, @@ -28,7 +32,7 @@ impl Picker { }) } - pub async fn list_displays(&self) -> anyhow::Result> { + pub async fn list_displays(&self) -> anyhow::Result> { let mut configs = self.display_configs.lock().await; let displays = Display::all() @@ -64,12 +68,12 @@ impl Picker { Ok(bitmap_string_list) } - pub async fn preview_display_by_config(config: DisplayConfig) -> anyhow::Result { + pub async fn preview_display_by_config(config: DisplayConfig) -> anyhow::Result { let start = time::Instant::now(); let mut picker = DisplayPicker::from_config(config)?; let screenshot = picker.take_screenshot()?; info!("Take Screenshot Spend: {}", start.elapsed()); - anyhow::Ok(screenshot.to_webp_base64().await) + anyhow::Ok(screenshot.to_dto().await) } } diff --git a/src-tauri/src/picker/screenshot.rs b/src-tauri/src/picker/screenshot.rs index 31a713f..b054c58 100644 --- a/src-tauri/src/picker/screenshot.rs +++ b/src-tauri/src/picker/screenshot.rs @@ -1,101 +1,233 @@ -use std::ops::Range; +use std::iter; use color_space::{Hsv, Rgb}; use either::Either; +use serde::{Deserialize, Serialize}; use super::{ config::{DisplayConfig, LedStripConfig}, led_color::LedColor, }; -#[derive(Clone)] +type Point = (usize, usize); +type LedSamplePoints = Vec; + +#[derive(Clone, Serialize, Deserialize, Debug)] +struct ScreenSamplePoints { + pub top: Vec, + pub bottom: Vec, + pub left: Vec, + pub right: Vec, +} + +#[derive(Clone, Serialize, Deserialize, Debug)] pub struct Screenshot { bitmap: Vec, config: DisplayConfig, + sample_points: ScreenSamplePoints, } impl Screenshot { pub fn new(bitmap: Vec, config: DisplayConfig) -> Self { - Self { bitmap, config } - } - - pub async fn get_top_colors(&self) -> anyhow::Result> { - self.get_x_colors(XPosition::Top, self.config.top_led_strip) - .await - } - pub async fn get_bottom_colors(&self) -> anyhow::Result> { - self.get_x_colors(XPosition::Bottom, self.config.bottom_led_strip) - .await - } - - pub fn get_top_of_led_start_at(&self) -> usize { - self.config.top_led_strip.global_start_position - } - pub fn get_top_of_led_end_at(&self) -> usize { - self.config.top_led_strip.global_end_position - } - - async fn get_x_colors( - &self, - position: XPosition, - strip_config: LedStripConfig, - ) -> anyhow::Result> { - let bitmap = &self.bitmap; - let number_of_leds = strip_config - .global_start_position - .abs_diff(strip_config.global_end_position) - + 1; - if number_of_leds == 0 { - return Ok(vec![]); + Self { + bitmap, + config, + sample_points: Self::get_sample_points(config), } - let cell_size_x = self.config.display_width / number_of_leds; - let cell_size_y = self.config.display_height / 8; - let cell_size = cell_size_x * cell_size_y; - let y_range = match position { - XPosition::Top => 20..cell_size_y + 20, - XPosition::Bottom => { - self.config.display_height - 20 - cell_size_y..self.config.display_height - 20 + } + + fn get_sample_points(config: DisplayConfig) -> ScreenSamplePoints { + let top = match config.top_led_strip { + Some(led_strip_config) => Self::get_one_edge_sample_points( + config.display_height / 8, + config.display_width, + led_strip_config + .global_start_position + .abs_diff(led_strip_config.global_end_position), + 5, + ), + None => { + vec![] } - } - .step_by(5); - - let x_range = if strip_config.global_start_position < strip_config.global_end_position { - Either::Left(strip_config.global_start_position..=strip_config.global_end_position) - } else { - Either::Right( - (strip_config.global_end_position..=strip_config.global_start_position).rev(), - ) }; - let mut colors = Vec::new(); - let stride = bitmap.len() / self.config.display_height; + let bottom: Vec = match config.top_led_strip { + Some(led_strip_config) => { + let points = Self::get_one_edge_sample_points( + config.display_height / 9, + config.display_width, + led_strip_config + .global_start_position + .abs_diff(led_strip_config.global_end_position), + 5, + ); + points + .into_iter() + .map(|groups| -> Vec { + groups + .into_iter() + .map(|(x, y)| (x, config.display_height - y)) + .collect() + }) + .collect() + } + None => { + vec![] + } + }; - for pos in x_range { + let left: Vec = match config.top_led_strip { + Some(led_strip_config) => { + let points = Self::get_one_edge_sample_points( + config.display_width / 16, + config.display_height, + led_strip_config + .global_start_position + .abs_diff(led_strip_config.global_end_position), + 5, + ); + points + .into_iter() + .map(|groups| -> Vec { + groups.into_iter().map(|(x, y)| (y, x)).collect() + }) + .collect() + } + None => { + vec![] + } + }; + + let right: Vec = match config.top_led_strip { + Some(led_strip_config) => { + let points = Self::get_one_edge_sample_points( + config.display_width / 16, + config.display_height, + led_strip_config + .global_start_position + .abs_diff(led_strip_config.global_end_position), + 5, + ); + points + .into_iter() + .map(|groups| -> Vec { + groups + .into_iter() + .map(|(x, y)| (y, config.display_width - x)) + .collect() + }) + .collect() + } + None => { + vec![] + } + }; + + ScreenSamplePoints { + top, + bottom, + left, + right, + } + } + + fn get_one_edge_sample_points( + width: usize, + length: usize, + leds: usize, + single_axis_points: usize, + ) -> Vec { + let cell_size_x = length as f64 / single_axis_points as f64 / leds as f64; + let cell_size_y = width / single_axis_points; + + let point_start_y = cell_size_y / 2; + let point_start_x = cell_size_x / 2.0; + let point_y_list: Vec = (point_start_y..width).step_by(cell_size_y).collect(); + let point_x_list: Vec = iter::successors(Some(point_start_x), |i| { + let next = i + cell_size_x; + (next < (width as f64)).then_some(next) + }) + .map(|i| i as usize) + .collect(); + + let points: Vec = point_x_list + .into_iter() + .zip(point_y_list.into_iter()) + .collect(); + points + .chunks(single_axis_points * single_axis_points) + .into_iter() + .map(|points| Vec::from(points)) + .collect() + } + + pub fn get_colors(&self) -> DisplayColorsOfLedStrips { + let top = self + .get_one_edge_colors(&self.sample_points.top) + .into_iter() + .flat_map(|color| color.get_rgb()) + .collect(); + let bottom = self + .get_one_edge_colors(&self.sample_points.bottom) + .into_iter() + .flat_map(|color| color.get_rgb()) + .collect(); + let left = self + .get_one_edge_colors(&self.sample_points.left) + .into_iter() + .flat_map(|color| color.get_rgb()) + .collect(); + let right = self + .get_one_edge_colors(&self.sample_points.right) + .into_iter() + .flat_map(|color| color.get_rgb()) + .collect(); + DisplayColorsOfLedStrips { + top, + bottom, + left, + right, + } + } + + pub fn get_one_edge_colors( + &self, + sample_points_of_leds: &Vec, + ) -> Vec { + let mut colors = vec![]; + for led_points in sample_points_of_leds { let mut r = 0.0; let mut g = 0.0; let mut b = 0.0; - let mut count = 0; - for x in (pos * cell_size_x..(pos + 1) * cell_size_x).step_by(5) { - for y in y_range.to_owned() { - let i = stride * y + 4 * x; - r += bitmap[i + 2] as f64; - g += bitmap[i + 1] as f64; - b += bitmap[i] as f64; - count+=1; - } + let len = led_points.len() as f64; + for (x, y) in led_points { + let position = (x + y * self.config.display_width) * 4; + r += self.bitmap[position + 2] as f64; + g += self.bitmap[position + 1] as f64; + b += self.bitmap[position] as f64; } - let rgb = Rgb::new( - r / count as f64, - g / count as f64, - b / count as f64, - ); - let hsv = Hsv::from(rgb); - // info!("HSV: {:?}", [hsv.h, hsv.s, hsv.v]); - let color = LedColor::from_hsv(hsv.h, hsv.s, hsv.v); + let color = LedColor::new((r / len) as u8, (g / len) as u8, (b / len) as u8); // paris::info!("color: {:?}", color.get_rgb()); colors.push(color); } - return Ok(colors); + colors + } + + pub fn get_top_colors(&self) -> Vec { + self.get_one_edge_colors(&self.sample_points.top) + } + + pub fn get_top_of_led_start_at(&self) -> usize { + self.config + .top_led_strip + .and_then(|c| Some(c.global_start_position)) + .unwrap_or(0) + } + pub fn get_top_of_led_end_at(&self) -> usize { + self.config + .top_led_strip + .and_then(|c| Some(c.global_end_position)) + .unwrap_or(0) } pub async fn to_webp_base64(&self) -> String { @@ -119,9 +251,30 @@ impl Screenshot { .encode(100.0); return base64::encode(&*webp_memory); } + + pub async fn to_dto(&self) -> ScreenshotDto { + let encode_image = self.to_webp_base64().await; + let config = self.config.clone(); + let colors = self.get_colors(); + ScreenshotDto { + encode_image, + config, + colors, + } + } } -enum XPosition { - Top, - Bottom, +#[derive(Clone, Serialize, Deserialize, Debug)] +pub struct ScreenshotDto { + pub config: DisplayConfig, + pub encode_image: String, + pub colors: DisplayColorsOfLedStrips, +} + +#[derive(Clone, Serialize, Deserialize, Debug)] +pub struct DisplayColorsOfLedStrips { + pub top: Vec, + pub bottom: Vec, + pub left: Vec, + pub right: Vec, }