feature/gui-configuration:支持从 GUI 配置程序。 #4

Merged
Ivan merged 19 commits from feature/gui-configuration into master 2023-01-26 23:44:19 +08:00
6 changed files with 278 additions and 133 deletions
Showing only changes of commit 4ad78ae5cc - Show all commits

View File

@ -118,16 +118,21 @@ impl CoreManager {
let mut global_colors = HashMap::new(); let mut global_colors = HashMap::new();
while let Some(screenshot) = rx.recv().await { while let Some(screenshot) = rx.recv().await {
let start_at = Instant::now(); let start_at = Instant::now();
match screenshot.get_top_colors().await { let colors = screenshot.get_top_colors();
Ok(colors) => { let start = screenshot
let start = screenshot.get_top_of_led_start_at().min(screenshot.get_top_of_led_end_at()); .get_top_of_led_start_at()
.min(screenshot.get_top_of_led_end_at());
let colors_len = colors.len(); let colors_len = colors.len();
for (index, color) in colors.into_iter().enumerate() { for (index, color) in colors.into_iter().enumerate() {
global_colors.insert(index + start, color); global_colors.insert(index + start, color);
} }
info!("led count: {}, spend: {:?}", global_colors.len(), start_at.elapsed()); info!(
"led count: {}, spend: {:?}",
global_colors.len(),
start_at.elapsed()
);
if global_colors.len() == 60 { if global_colors.len() == 60 {
let mut colors = vec![]; let mut colors = vec![];
@ -148,9 +153,6 @@ impl CoreManager {
} }
} }
} }
Err(_) => {}
};
}
}); });
join_all(futs).await; join_all(futs).await;

View File

@ -2,6 +2,7 @@
all(not(debug_assertions), target_os = "windows"), all(not(debug_assertions), target_os = "windows"),
windows_subsystem = "windows" windows_subsystem = "windows"
)] )]
#![feature(bool_to_option)]
mod core; mod core;
mod picker; mod picker;
@ -11,10 +12,11 @@ use crate::core::AmbientLightMode;
use crate::core::CoreManager; use crate::core::CoreManager;
use paris::*; use paris::*;
use picker::manager::Picker; use picker::manager::Picker;
use picker::screenshot::ScreenshotDto;
use std::vec; use std::vec;
#[tauri::command] #[tauri::command]
async fn take_snapshot() -> Vec<String> { async fn take_snapshot() -> Vec<ScreenshotDto> {
let manager = Picker::global(); let manager = Picker::global();
let start = time::Instant::now(); let start = time::Instant::now();
@ -29,7 +31,7 @@ async fn take_snapshot() -> Vec<String> {
vec![] vec![]
} }
}; };
info!("截图花费 {} s", start.elapsed().as_seconds_f32()); info!("截图耗时 {} s", start.elapsed().as_seconds_f32());
base64_bitmap_list base64_bitmap_list
} }

View File

@ -12,10 +12,10 @@ pub struct DisplayConfig {
pub index_of_display: usize, pub index_of_display: usize,
pub display_width: usize, pub display_width: usize,
pub display_height: usize, pub display_height: usize,
pub top_led_strip: LedStripConfig, pub top_led_strip: Option<LedStripConfig>,
pub bottom_led_strip: LedStripConfig, pub bottom_led_strip: Option<LedStripConfig>,
pub left_led_strip: LedStripConfig, pub left_led_strip: Option<LedStripConfig>,
pub right_led_strip: LedStripConfig, pub right_led_strip: Option<LedStripConfig>,
} }
impl DisplayConfig { impl DisplayConfig {
@ -24,26 +24,10 @@ impl DisplayConfig {
index_of_display, index_of_display,
display_width, display_width,
display_height, display_height,
top_led_strip: LedStripConfig { top_led_strip: None,
index: 0, bottom_led_strip: None,
global_start_position: 0, left_led_strip: None,
global_end_position: 0, right_led_strip: None,
},
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,
},
} }
} }
} }

View File

@ -1,7 +1,7 @@
use color_space::{Hsv, Rgb}; use color_space::{Hsv, Rgb};
use serde::Serialize; use serde::Serialize;
#[derive(Clone, Copy)] #[derive(Clone, Copy, Debug)]
pub struct LedColor { pub struct LedColor {
bits: [u8; 3], bits: [u8; 3],
} }

View File

@ -2,12 +2,16 @@ use futures::{stream::FuturesUnordered, StreamExt};
use once_cell::sync::OnceCell; use once_cell::sync::OnceCell;
use paris::info; use paris::info;
use scrap::Display; use scrap::Display;
use std::{sync::Arc, borrow::Borrow}; use std::{borrow::Borrow, sync::Arc};
use tokio::{sync::Mutex, task}; use tokio::{sync::Mutex, task};
use crate::picker::{config, screen::Screen}; 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 struct Picker {
pub screens: Arc<Mutex<Vec<Screen>>>, pub screens: Arc<Mutex<Vec<Screen>>>,
@ -28,7 +32,7 @@ impl Picker {
}) })
} }
pub async fn list_displays(&self) -> anyhow::Result<Vec<String>> { pub async fn list_displays(&self) -> anyhow::Result<Vec<ScreenshotDto>> {
let mut configs = self.display_configs.lock().await; let mut configs = self.display_configs.lock().await;
let displays = Display::all() let displays = Display::all()
@ -64,12 +68,12 @@ impl Picker {
Ok(bitmap_string_list) Ok(bitmap_string_list)
} }
pub async fn preview_display_by_config(config: DisplayConfig) -> anyhow::Result<String> { pub async fn preview_display_by_config(config: DisplayConfig) -> anyhow::Result<ScreenshotDto> {
let start = time::Instant::now(); let start = time::Instant::now();
let mut picker = DisplayPicker::from_config(config)?; let mut picker = DisplayPicker::from_config(config)?;
let screenshot = picker.take_screenshot()?; let screenshot = picker.take_screenshot()?;
info!("Take Screenshot Spend: {}", start.elapsed()); info!("Take Screenshot Spend: {}", start.elapsed());
anyhow::Ok(screenshot.to_webp_base64().await) anyhow::Ok(screenshot.to_dto().await)
} }
} }

View File

@ -1,101 +1,233 @@
use std::ops::Range; use std::iter;
use color_space::{Hsv, Rgb}; use color_space::{Hsv, Rgb};
use either::Either; use either::Either;
use serde::{Deserialize, Serialize};
use super::{ use super::{
config::{DisplayConfig, LedStripConfig}, config::{DisplayConfig, LedStripConfig},
led_color::LedColor, led_color::LedColor,
}; };
#[derive(Clone)] type Point = (usize, usize);
type LedSamplePoints = Vec<Point>;
#[derive(Clone, Serialize, Deserialize, Debug)]
struct ScreenSamplePoints {
pub top: Vec<LedSamplePoints>,
pub bottom: Vec<LedSamplePoints>,
pub left: Vec<LedSamplePoints>,
pub right: Vec<LedSamplePoints>,
}
#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct Screenshot { pub struct Screenshot {
bitmap: Vec<u8>, bitmap: Vec<u8>,
config: DisplayConfig, config: DisplayConfig,
sample_points: ScreenSamplePoints,
} }
impl Screenshot { impl Screenshot {
pub fn new(bitmap: Vec<u8>, config: DisplayConfig) -> Self { pub fn new(bitmap: Vec<u8>, config: DisplayConfig) -> Self {
Self { bitmap, config } Self {
bitmap,
config,
sample_points: Self::get_sample_points(config),
}
} }
pub async fn get_top_colors(&self) -> anyhow::Result<Vec<LedColor>> { fn get_sample_points(config: DisplayConfig) -> ScreenSamplePoints {
self.get_x_colors(XPosition::Top, self.config.top_led_strip) let top = match config.top_led_strip {
.await Some(led_strip_config) => Self::get_one_edge_sample_points(
} config.display_height / 8,
pub async fn get_bottom_colors(&self) -> anyhow::Result<Vec<LedColor>> { config.display_width,
self.get_x_colors(XPosition::Bottom, self.config.bottom_led_strip) led_strip_config
.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<Vec<LedColor>> {
let bitmap = &self.bitmap;
let number_of_leds = strip_config
.global_start_position .global_start_position
.abs_diff(strip_config.global_end_position) .abs_diff(led_strip_config.global_end_position),
+ 1; 5,
if number_of_leds == 0 { ),
return Ok(vec![]); None => {
vec![]
} }
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
}
}
.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 bottom: Vec<LedSamplePoints> = match config.top_led_strip {
let stride = bitmap.len() / self.config.display_height; 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<Point> {
groups
.into_iter()
.map(|(x, y)| (x, config.display_height - y))
.collect()
})
.collect()
}
None => {
vec![]
}
};
for pos in x_range { let left: Vec<LedSamplePoints> = 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<Point> {
groups.into_iter().map(|(x, y)| (y, x)).collect()
})
.collect()
}
None => {
vec![]
}
};
let right: Vec<LedSamplePoints> = 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<Point> {
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<LedSamplePoints> {
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<usize> = (point_start_y..width).step_by(cell_size_y).collect();
let point_x_list: Vec<usize> = 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> = 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<LedSamplePoints>,
) -> Vec<LedColor> {
let mut colors = vec![];
for led_points in sample_points_of_leds {
let mut r = 0.0; let mut r = 0.0;
let mut g = 0.0; let mut g = 0.0;
let mut b = 0.0; let mut b = 0.0;
let mut count = 0; let len = led_points.len() as f64;
for x in (pos * cell_size_x..(pos + 1) * cell_size_x).step_by(5) { for (x, y) in led_points {
for y in y_range.to_owned() { let position = (x + y * self.config.display_width) * 4;
let i = stride * y + 4 * x; r += self.bitmap[position + 2] as f64;
r += bitmap[i + 2] as f64; g += self.bitmap[position + 1] as f64;
g += bitmap[i + 1] as f64; b += self.bitmap[position] as f64;
b += bitmap[i] as f64;
count+=1;
} }
} let color = LedColor::new((r / len) as u8, (g / len) as u8, (b / len) as u8);
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);
// paris::info!("color: {:?}", color.get_rgb()); // paris::info!("color: {:?}", color.get_rgb());
colors.push(color); colors.push(color);
} }
return Ok(colors); colors
}
pub fn get_top_colors(&self) -> Vec<LedColor> {
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 { pub async fn to_webp_base64(&self) -> String {
@ -119,9 +251,30 @@ impl Screenshot {
.encode(100.0); .encode(100.0);
return base64::encode(&*webp_memory); 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 { #[derive(Clone, Serialize, Deserialize, Debug)]
Top, pub struct ScreenshotDto {
Bottom, pub config: DisplayConfig,
pub encode_image: String,
pub colors: DisplayColorsOfLedStrips,
}
#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct DisplayColorsOfLedStrips {
pub top: Vec<u8>,
pub bottom: Vec<u8>,
pub left: Vec<u8>,
pub right: Vec<u8>,
} }