pref: 针对 HiDPI 屏幕捕获的优化。

This commit is contained in:
Ivan Li 2023-04-05 12:25:14 +08:00
parent 3ec983cd95
commit 6c3ce607e0
11 changed files with 486 additions and 214 deletions

1
src-tauri/Cargo.lock generated
View File

@ -2790,6 +2790,7 @@ version = "0.0.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"color_space", "color_space",
"core-foundation",
"core-graphics", "core-graphics",
"display-info", "display-info",
"env_logger", "env_logger",

View File

@ -31,6 +31,7 @@ toml = "0.7.3"
paho-mqtt = "0.12.1" paho-mqtt = "0.12.1"
time = {version="0.3.20", features= ["formatting"] } time = {version="0.3.20", features= ["formatting"] }
itertools = "0.10.5" itertools = "0.10.5"
core-foundation = "0.9.3"
[features] [features]
# this feature is used for production builds or when `devPath` points to the filesystem # this feature is used for production builds or when `devPath` points to the filesystem

View File

@ -21,6 +21,8 @@ impl ConfigManager {
let configs = LedStripConfigGroup::read_config().await.unwrap(); let configs = LedStripConfigGroup::read_config().await.unwrap();
let (config_update_sender, config_update_receiver) = let (config_update_sender, config_update_receiver) =
tokio::sync::watch::channel(configs.clone()); tokio::sync::watch::channel(configs.clone());
config_update_sender.send(configs.clone()).unwrap();
ConfigManager { ConfigManager {
config: Arc::new(RwLock::new(configs)), config: Arc::new(RwLock::new(configs)),
config_update_receiver, config_update_receiver,

View File

@ -1,26 +1,31 @@
use std::{collections::HashMap, sync::Arc, time::Duration}; use std::{collections::HashMap, sync::Arc, time::Duration};
use paris::{info, warn}; use paris::warn;
use tauri::async_runtime::{Mutex, RwLock}; use tauri::async_runtime::RwLock;
use tokio::{sync::watch, time::sleep}; use tokio::{
sync::{broadcast, watch},
time::sleep,
};
use crate::{ use crate::{
ambient_light::{config, ConfigManager}, ambient_light::{config, ConfigManager},
led_color::LedColor,
rpc::MqttRpc, rpc::MqttRpc,
screenshot::{self, Screenshot}, screenshot::LedSamplePoints,
screenshot_manager::ScreenshotManager, screenshot_manager::{self, ScreenshotManager},
}; };
use itertools::Itertools; use itertools::Itertools;
use super::{LedStripConfigGroup, SamplePointConfig}; use super::{LedStripConfigGroup, SamplePointConfig, SamplePointMapper};
pub struct LedColorsPublisher { pub struct LedColorsPublisher {
sorted_colors_rx: Arc<RwLock<watch::Receiver<Vec<u8>>>>, sorted_colors_rx: Arc<RwLock<watch::Receiver<Vec<u8>>>>,
sorted_colors_tx: Arc<RwLock<watch::Sender<Vec<u8>>>>, sorted_colors_tx: Arc<RwLock<watch::Sender<Vec<u8>>>>,
colors_rx: Arc<RwLock<watch::Receiver<Vec<LedColor>>>>, colors_rx: Arc<RwLock<watch::Receiver<Vec<u8>>>>,
colors_tx: Arc<RwLock<watch::Sender<Vec<LedColor>>>>, colors_tx: Arc<RwLock<watch::Sender<Vec<u8>>>>,
display_colors_rx: Arc<RwLock<broadcast::Receiver<(u32, Vec<u8>)>>>,
display_colors_tx: Arc<RwLock<broadcast::Sender<(u32, Vec<u8>)>>>,
inner_tasks_version: Arc<RwLock<usize>>,
} }
impl LedColorsPublisher { impl LedColorsPublisher {
@ -30,6 +35,7 @@ impl LedColorsPublisher {
let (sorted_tx, sorted_rx) = watch::channel(Vec::new()); let (sorted_tx, sorted_rx) = watch::channel(Vec::new());
let (tx, rx) = watch::channel(Vec::new()); let (tx, rx) = watch::channel(Vec::new());
let (display_colors_tx, display_colors_rx) = broadcast::channel(8);
LED_COLORS_PUBLISHER_GLOBAL LED_COLORS_PUBLISHER_GLOBAL
.get_or_init(|| async { .get_or_init(|| async {
@ -38,23 +44,172 @@ impl LedColorsPublisher {
sorted_colors_tx: Arc::new(RwLock::new(sorted_tx)), sorted_colors_tx: Arc::new(RwLock::new(sorted_tx)),
colors_rx: Arc::new(RwLock::new(rx)), colors_rx: Arc::new(RwLock::new(rx)),
colors_tx: Arc::new(RwLock::new(tx)), colors_tx: Arc::new(RwLock::new(tx)),
display_colors_rx: Arc::new(RwLock::new(display_colors_rx)),
display_colors_tx: Arc::new(RwLock::new(display_colors_tx)),
inner_tasks_version: Arc::new(RwLock::new(0)),
} }
}) })
.await .await
} }
pub fn start(&self) { fn start_one_display_colors_fetcher(
let sorted_colors_tx = self.sorted_colors_tx.clone(); &self,
let colors_tx = self.colors_tx.clone(); display_id: u32,
sample_points: Vec<Vec<LedSamplePoints>>,
) {
let display_colors_tx = self.display_colors_tx.clone();
let internal_tasks_version = self.inner_tasks_version.clone();
tokio::spawn(async move { tokio::spawn(async move {
let display_colors_tx = display_colors_tx.read().await.clone();
let colors = screenshot_manager::get_display_colors(display_id, &sample_points);
if let Err(err) = colors {
warn!("Failed to get colors: {}", err);
return;
}
let mut start: tokio::time::Instant = tokio::time::Instant::now();
let mut interval = tokio::time::interval(Duration::from_millis(66));
let init_version = internal_tasks_version.read().await.clone();
loop { loop {
interval.tick().await;
tokio::time::sleep(Duration::from_millis(1)).await;
if internal_tasks_version.read().await.clone() != init_version {
log::info!(
"inner task version changed, stop. {} != {}",
internal_tasks_version.read().await.clone(),
init_version
);
break;
}
// log::info!("tick: {}ms", start.elapsed().as_millis());
start = tokio::time::Instant::now();
let colors = screenshot_manager::get_display_colors(display_id, &sample_points);
if let Err(err) = colors {
warn!("Failed to get colors: {}", err);
sleep(Duration::from_millis(100)).await;
continue;
}
let colors = colors.unwrap();
let color_len = colors.len();
match display_colors_tx.send((
display_id,
colors
.into_iter()
.map(|color| color.get_rgb())
.flatten()
.collect::<Vec<_>>(),
)) {
Ok(_) => {
// log::info!("sent colors: {:?}", color_len);
}
Err(err) => {
warn!("Failed to send display_colors: {}", err);
}
};
}
});
}
fn start_all_colors_worker(&self, display_ids: Vec<u32>, mappers: Vec<SamplePointMapper>) {
let sorted_colors_tx = self.sorted_colors_tx.clone();
let colors_tx = self.colors_tx.clone();
let display_colors_rx = self.display_colors_rx.clone();
tokio::spawn(async move {
for _ in 0..10 {
let mut rx = display_colors_rx.read().await.resubscribe();
let sorted_colors_tx = sorted_colors_tx.write().await; let sorted_colors_tx = sorted_colors_tx.write().await;
let colors_tx = colors_tx.write().await; let colors_tx = colors_tx.write().await;
let screenshot_manager = ScreenshotManager::global().await;
let mut all_colors: Vec<Option<Vec<u8>>> = vec![None; display_ids.len()];
let mut start: tokio::time::Instant = tokio::time::Instant::now();
log::info!("start all_colors_worker");
loop {
// log::info!("display_colors_rx changed");
let color_info = rx.recv().await;
if let Err(err) = color_info {
match err {
broadcast::error::RecvError::Closed => {
break;
}
broadcast::error::RecvError::Lagged(_) => {
warn!("display_colors_rx lagged");
continue;
}
}
}
let (display_id, colors) = color_info.unwrap();
let index = display_ids.iter().position(|id| *id == display_id);
if index.is_none() {
warn!("display id not found");
continue;
}
all_colors[index.unwrap()] = Some(colors);
if all_colors.iter().all(|color| color.is_some()) {
let flatten_colors = all_colors
.clone()
.into_iter()
.flat_map(|c| c.unwrap())
.collect::<Vec<_>>();
match colors_tx.send(flatten_colors.clone()) {
Ok(_) => {}
Err(err) => {
warn!("Failed to send colors: {}", err);
}
};
let sorted_colors =
ScreenshotManager::get_sorted_colors(&flatten_colors, &mappers);
match sorted_colors_tx.send(sorted_colors) {
Ok(_) => {}
Err(err) => {
warn!("Failed to send sorted colors: {}", err);
}
};
log::info!("tick: {}ms", start.elapsed().as_millis());
start = tokio::time::Instant::now();
}
}
}
});
}
pub fn start(&self) {
let inner_tasks_version = self.inner_tasks_version.clone();
tokio::spawn(async move {
let publisher = Self::global().await;
let mut inner_tasks_version = inner_tasks_version.write().await;
*inner_tasks_version = inner_tasks_version.overflowing_add(1).0;
let config_manager = ConfigManager::global().await; let config_manager = ConfigManager::global().await;
let config_receiver = config_manager.clone_config_update_receiver(); let mut config_receiver = config_manager.clone_config_update_receiver();
log::info!("waiting for config update...");
while config_receiver.changed().await.is_ok() {
log::info!("config updated, restart inner tasks...");
let configs = config_receiver.borrow().clone(); let configs = config_receiver.borrow().clone();
let configs = Self::get_colors_configs(&configs).await; let configs = Self::get_colors_configs(&configs).await;
@ -66,73 +221,113 @@ impl LedColorsPublisher {
let configs = configs.unwrap(); let configs = configs.unwrap();
let mut merged_screenshot_receiver = for sample_point_group in configs.sample_point_groups.clone() {
screenshot_manager.clone_merged_screenshot_rx().await; let display_id = sample_point_group.display_id;
let sample_points = sample_point_group.points;
let mut screenshots = HashMap::new(); publisher.start_one_display_colors_fetcher(display_id, sample_points);
loop {
let screenshot = merged_screenshot_receiver.recv().await;
if let Err(err) = screenshot {
match err {
tokio::sync::broadcast::error::RecvError::Closed => {
warn!("closed");
continue;
},
tokio::sync::broadcast::error::RecvError::Lagged(_) => {
warn!("lagged");
continue;
},
}
} }
let screenshot = screenshot.unwrap(); let display_ids = configs.sample_point_groups;
// log::info!("got screenshot: {:?}", screenshot.display_id); publisher.start_all_colors_worker(
display_ids.iter().map(|c| c.display_id).collect(),
configs.mappers,
);
screenshots.insert(screenshot.display_id, screenshot); break;
if screenshots.len() == configs.sample_point_groups.len() {
{
let screenshots = configs
.sample_point_groups
.iter()
.map(|strip| screenshots.get(&strip.display_id).unwrap())
.collect::<Vec<_>>();
let colors = screenshot_manager
.get_all_colors(&configs.sample_point_groups, &screenshots)
.await;
let sorted_colors =
ScreenshotManager::get_sorted_colors(&colors, &configs.mappers)
.await;
match colors_tx.send(colors) {
Ok(_) => {
// log::info!("colors updated");
}
Err(_) => {
warn!("colors update failed");
}
}
match sorted_colors_tx.send(sorted_colors) {
Ok(_) => {
// log::info!("colors updated");
}
Err(_) => {
warn!("colors update failed");
}
}
}
screenshots.clear();
}
}
} }
}); });
// tokio::spawn(async move {
// loop {
// let sorted_colors_tx = sorted_colors_tx.write().await;
// let colors_tx = colors_tx.write().await;
// let screenshot_manager = ScreenshotManager::global().await;
// let config_manager = ConfigManager::global().await;
// let config_receiver = config_manager.clone_config_update_receiver();
// let configs = config_receiver.borrow().clone();
// let configs = Self::get_colors_configs(&configs).await;
// if let Err(err) = configs {
// warn!("Failed to get configs: {}", err);
// sleep(Duration::from_millis(100)).await;
// continue;
// }
// let configs = configs.unwrap();
// let mut merged_screenshot_receiver =
// screenshot_manager.clone_merged_screenshot_rx().await;
// let mut screenshots = HashMap::new();
// // let mut start = tokio::time::Instant::now();
// loop {
// let screenshot = merged_screenshot_receiver.recv().await;
// if let Err(err) = screenshot {
// match err {
// tokio::sync::broadcast::error::RecvError::Closed => {
// warn!("closed");
// continue;
// }
// tokio::sync::broadcast::error::RecvError::Lagged(_) => {
// warn!("lagged");
// continue;
// }
// }
// }
// let screenshot = screenshot.unwrap();
// // log::info!("got screenshot: {:?}", screenshot.display_id);
// screenshots.insert(screenshot.display_id, screenshot);
// if screenshots.len() == configs.sample_point_groups.len() {
// // log::info!("{}", start.elapsed().as_millis().to_string());
// {
// let screenshots = configs
// .sample_point_groups
// .iter()
// .map(|strip| screenshots.get(&strip.display_id).unwrap())
// .collect::<Vec<_>>();
// let colors = screenshot_manager
// .get_all_colors(&configs.sample_point_groups, &screenshots)
// .await;
// let sorted_colors =
// ScreenshotManager::get_sorted_colors(&colors, &configs.mappers)
// .await;
// match colors_tx.send(colors) {
// Ok(_) => {
// // log::info!("colors updated");
// }
// Err(_) => {
// warn!("colors update failed");
// }
// }
// match sorted_colors_tx.send(sorted_colors) {
// Ok(_) => {
// // log::info!("colors updated");
// }
// Err(_) => {
// warn!("colors update failed");
// }
// }
// }
// // screenshots.clear();
// // start = tokio::time::Instant::now();
// }
// }
// }
// });
let rx = self.sorted_colors_rx.clone(); let rx = self.sorted_colors_rx.clone();
tokio::spawn(async move { tokio::spawn(async move {
let mut rx = rx.read().await.clone(); let mut rx = rx.read().await.clone();
@ -149,7 +344,7 @@ impl LedColorsPublisher {
match Self::send_colors(colors).await { match Self::send_colors(colors).await {
Ok(_) => { Ok(_) => {
log::info!("colors sent. len: {}", len); // log::info!("colors sent. len: {}", len);
} }
Err(err) => { Err(err) => {
warn!("colors send failed: {}", err); warn!("colors send failed: {}", err);
@ -173,8 +368,6 @@ impl LedColorsPublisher {
) -> anyhow::Result<AllColorConfig> { ) -> anyhow::Result<AllColorConfig> {
let screenshot_manager = ScreenshotManager::global().await; let screenshot_manager = ScreenshotManager::global().await;
let channels = screenshot_manager.channels.read().await;
let display_ids = configs let display_ids = configs
.strips .strips
.iter() .iter()
@ -184,21 +377,36 @@ impl LedColorsPublisher {
let mappers = configs.mappers.clone(); let mappers = configs.mappers.clone();
let mut local_rx_list = Vec::new();
let mut colors_configs = Vec::new(); let mut colors_configs = Vec::new();
for display_id in display_ids.clone().iter() { let mut merged_screenshot_receiver = screenshot_manager.clone_merged_screenshot_rx().await;
let display_id = *display_id;
let channel = channels.get(&display_id); let mut screenshots = HashMap::new();
if channel.is_none() {
anyhow::bail!("no channel for display_id: {}", display_id); loop {
log::info!("waiting merged screenshot...");
let screenshot = merged_screenshot_receiver.recv().await;
if let Err(err) = screenshot {
match err {
tokio::sync::broadcast::error::RecvError::Closed => {
warn!("closed");
continue;
}
tokio::sync::broadcast::error::RecvError::Lagged(_) => {
warn!("lagged");
continue;
}
}
} }
let channel_rx = channel.unwrap().clone(); let screenshot = screenshot.unwrap();
// log::info!("got screenshot: {:?}", screenshot.display_id);
local_rx_list.push(channel.unwrap().clone()); screenshots.insert(screenshot.display_id, screenshot);
if screenshots.len() == display_ids.len() {
for display_id in display_ids {
let led_strip_configs: Vec<_> = configs let led_strip_configs: Vec<_> = configs
.strips .strips
.iter() .iter()
@ -209,18 +417,16 @@ impl LedColorsPublisher {
warn!("no led strip config for display_id: {}", display_id); warn!("no led strip config for display_id: {}", display_id);
continue; continue;
} }
let rx = channel_rx.to_owned();
let screenshot = rx.borrow().clone(); let screenshot = screenshots.get(&display_id).unwrap();
log::debug!("screenshot updated: {:?}", display_id); log::debug!("screenshot updated: {:?}", display_id);
let points: Vec<_> = led_strip_configs let points: Vec<_> = led_strip_configs
.iter() .iter()
.map(|config| screenshot.get_sample_points(&config)) .map(|config| screenshot.get_sample_points(&config))
.flatten()
.collect(); .collect();
let colors_config = config::SamplePointConfig { display_id, points }; let colors_config = DisplaySamplePointGroup { display_id, points };
colors_configs.push(colors_config); colors_configs.push(colors_config);
} }
@ -228,18 +434,26 @@ impl LedColorsPublisher {
return Ok(AllColorConfig { return Ok(AllColorConfig {
sample_point_groups: colors_configs, sample_point_groups: colors_configs,
mappers, mappers,
screenshot_receivers: local_rx_list, // screenshot_receivers: local_rx_list,
}); });
} }
}
}
pub async fn clone_colors_receiver(&self) -> watch::Receiver<Vec<LedColor>> { pub async fn clone_colors_receiver(&self) -> watch::Receiver<Vec<u8>> {
self.colors_rx.read().await.clone() self.colors_rx.read().await.clone()
} }
} }
#[derive(Debug)] #[derive(Debug)]
pub struct AllColorConfig { pub struct AllColorConfig {
pub sample_point_groups: Vec<SamplePointConfig>, pub sample_point_groups: Vec<DisplaySamplePointGroup>,
pub mappers: Vec<config::SamplePointMapper>, pub mappers: Vec<config::SamplePointMapper>,
pub screenshot_receivers: Vec<watch::Receiver<Screenshot>>, // pub screenshot_receivers: Vec<watch::Receiver<Screenshot>>,
}
#[derive(Debug, Clone)]
pub struct DisplaySamplePointGroup {
pub display_id: u32,
pub points: Vec<Vec<LedSamplePoints>>,
} }

View File

@ -106,7 +106,7 @@ async fn get_one_edge_colors(
if let Some(rx) = channels.get(&display_id) { if let Some(rx) = channels.get(&display_id) {
let rx = rx.clone(); let rx = rx.clone();
let screenshot = rx.borrow().clone(); let screenshot = rx.borrow().clone();
let bytes = screenshot.bytes.read().await; let bytes = screenshot.bytes.read().await.to_owned();
let colors = let colors =
Screenshot::get_one_edge_colors(&sample_points, &bytes, screenshot.bytes_per_row); Screenshot::get_one_edge_colors(&sample_points, &bytes, screenshot.bytes_per_row);
Ok(colors) Ok(colors)

View File

@ -1,8 +1,9 @@
use std::iter; use std::cell::RefCell;
use std::{iter, cell::Ref};
use std::sync::Arc; use std::sync::Arc;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use tauri::async_runtime::RwLock; use tauri::async_runtime::{RwLock, Mutex};
use crate::{ambient_light::LedStripConfig, led_color::LedColor}; use crate::{ambient_light::LedStripConfig, led_color::LedColor};
@ -14,9 +15,10 @@ pub struct Screenshot {
pub bytes_per_row: usize, pub bytes_per_row: usize,
pub bytes: Arc<RwLock<Vec<u8>>>, pub bytes: Arc<RwLock<Vec<u8>>>,
pub scale_factor: f32, pub scale_factor: f32,
pub sample_points: ScreenSamplePoints,
} }
static SINGLE_AXIS_POINTS: usize = 5;
impl Screenshot { impl Screenshot {
pub fn new( pub fn new(
display_id: u32, display_id: u32,
@ -25,7 +27,6 @@ impl Screenshot {
bytes_per_row: usize, bytes_per_row: usize,
bytes: Vec<u8>, bytes: Vec<u8>,
scale_factor: f32, scale_factor: f32,
sample_points: ScreenSamplePoints,
) -> Self { ) -> Self {
Self { Self {
display_id, display_id,
@ -34,7 +35,6 @@ impl Screenshot {
bytes_per_row, bytes_per_row,
bytes: Arc::new(RwLock::new(bytes)), bytes: Arc::new(RwLock::new(bytes)),
scale_factor, scale_factor,
sample_points,
} }
} }
@ -44,10 +44,10 @@ impl Screenshot {
match config.border { match config.border {
crate::ambient_light::Border::Top => { crate::ambient_light::Border::Top => {
Self::get_one_edge_sample_points(height / 8, width, config.len, 5) Self::get_one_edge_sample_points(height / 18, width, config.len, SINGLE_AXIS_POINTS)
} }
crate::ambient_light::Border::Bottom => { crate::ambient_light::Border::Bottom => {
let points = Self::get_one_edge_sample_points(height / 9, width, config.len, 5); let points = Self::get_one_edge_sample_points(height / 18, width, config.len, SINGLE_AXIS_POINTS);
points points
.into_iter() .into_iter()
.map(|groups| -> Vec<Point> { .map(|groups| -> Vec<Point> {
@ -56,7 +56,7 @@ impl Screenshot {
.collect() .collect()
} }
crate::ambient_light::Border::Left => { crate::ambient_light::Border::Left => {
let points = Self::get_one_edge_sample_points(width / 16, height, config.len, 5); let points = Self::get_one_edge_sample_points(width / 32, height, config.len, SINGLE_AXIS_POINTS);
points points
.into_iter() .into_iter()
.map(|groups| -> Vec<Point> { .map(|groups| -> Vec<Point> {
@ -65,7 +65,7 @@ impl Screenshot {
.collect() .collect()
} }
crate::ambient_light::Border::Right => { crate::ambient_light::Border::Right => {
let points = Self::get_one_edge_sample_points(width / 16, height, config.len, 5); let points = Self::get_one_edge_sample_points(width / 32, height, config.len, SINGLE_AXIS_POINTS);
points points
.into_iter() .into_iter()
.map(|groups| -> Vec<Point> { .map(|groups| -> Vec<Point> {
@ -112,46 +112,6 @@ impl Screenshot {
.collect() .collect()
} }
pub async fn get_colors(&self) -> DisplayColorsOfLedStrips {
let bitmap = self.bytes.read().await;
let top =
Self::get_one_edge_colors(&self.sample_points.top, bitmap.as_ref(), self.bytes_per_row)
.into_iter()
.flat_map(|color| color.get_rgb())
.collect();
let bottom = Self::get_one_edge_colors(
&self.sample_points.bottom,
bitmap.as_ref(),
self.bytes_per_row,
)
.into_iter()
.flat_map(|color| color.get_rgb())
.collect();
let left = Self::get_one_edge_colors(
&self.sample_points.left,
bitmap.as_ref(),
self.bytes_per_row,
)
.into_iter()
.flat_map(|color| color.get_rgb())
.collect();
let right = Self::get_one_edge_colors(
&self.sample_points.right,
bitmap.as_ref(),
self.bytes_per_row,
)
.into_iter()
.flat_map(|color| color.get_rgb())
.collect();
DisplayColorsOfLedStrips {
top,
bottom,
left,
right,
}
}
pub fn get_one_edge_colors( pub fn get_one_edge_colors(
sample_points_of_leds: &Vec<LedSamplePoints>, sample_points_of_leds: &Vec<LedSamplePoints>,
bitmap: &Vec<u8>, bitmap: &Vec<u8>,
@ -176,6 +136,30 @@ impl Screenshot {
colors colors
} }
pub fn get_one_edge_colors_by_cg_image(
sample_points_of_leds: &Vec<LedSamplePoints>,
bitmap: core_foundation::data::CFData,
bytes_per_row: usize,
) -> Vec<LedColor> {
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 len = led_points.len() as f64;
for (x, y) in led_points {
// log::info!("x: {}, y: {}, bytes_per_row: {}", x, y, bytes_per_row);
let position = x * 4 + y * bytes_per_row;
b += bitmap[position] as f64;
g += bitmap[position + 1] as f64;
r += bitmap[position + 2] as f64;
}
let color = LedColor::new((r / len) as u8, (g / len) as u8, (b / len) as u8);
colors.push(color);
}
colors
}
pub async fn get_colors_by_sample_points( pub async fn get_colors_by_sample_points(
&self, &self,
points: &Vec<LedSamplePoints>, points: &Vec<LedSamplePoints>,

View File

@ -1,13 +1,16 @@
use std::cell::{Ref, RefCell};
use std::{collections::HashMap, sync::Arc}; use std::{collections::HashMap, sync::Arc};
use core_graphics::display::{ use core_graphics::display::{
kCGNullWindowID, kCGWindowImageDefault, kCGWindowListOptionOnScreenOnly, CGDisplay, kCGNullWindowID, kCGWindowImageDefault, kCGWindowListOptionOnScreenOnly, CGDisplay,
}; };
use core_graphics::geometry::{CGPoint, CGRect, CGSize};
use paris::warn; use paris::warn;
use tauri::async_runtime::RwLock; use tauri::async_runtime::RwLock;
use tokio::sync::{broadcast, watch, OnceCell}; use tokio::sync::{broadcast, watch, OnceCell};
use tokio::time::{self, Duration}; use tokio::time::{self, Duration};
use crate::screenshot::LedSamplePoints;
use crate::{ use crate::{
ambient_light::{SamplePointConfig, SamplePointMapper}, ambient_light::{SamplePointConfig, SamplePointMapper},
led_color::LedColor, led_color::LedColor,
@ -16,7 +19,6 @@ use crate::{
pub fn take_screenshot(display_id: u32, scale_factor: f32) -> anyhow::Result<Screenshot> { pub fn take_screenshot(display_id: u32, scale_factor: f32) -> anyhow::Result<Screenshot> {
log::debug!("take_screenshot"); log::debug!("take_screenshot");
// let start_at = std::time::Instant::now();
let cg_display = CGDisplay::new(display_id); let cg_display = CGDisplay::new(display_id);
let cg_image = CGDisplay::screenshot( let cg_image = CGDisplay::screenshot(
@ -26,7 +28,6 @@ pub fn take_screenshot(display_id: u32, scale_factor: f32) -> anyhow::Result<Scr
kCGWindowImageDefault, kCGWindowImageDefault,
) )
.ok_or_else(|| anyhow::anyhow!("Display#{}: take screenshot failed", display_id))?; .ok_or_else(|| anyhow::anyhow!("Display#{}: take screenshot failed", display_id))?;
// println!("take screenshot took {}ms", start_at.elapsed().as_millis());
let buffer = cg_image.data(); let buffer = cg_image.data();
let bytes_per_row = cg_image.bytes_per_row(); let bytes_per_row = cg_image.bytes_per_row();
@ -34,8 +35,7 @@ pub fn take_screenshot(display_id: u32, scale_factor: f32) -> anyhow::Result<Scr
let height = cg_image.height(); let height = cg_image.height();
let width = cg_image.width(); let width = cg_image.width();
let mut bytes = vec![0u8; buffer.len() as usize]; let bytes = buffer.bytes().to_owned();
bytes.copy_from_slice(&buffer);
Ok(Screenshot::new( Ok(Screenshot::new(
display_id, display_id,
@ -44,18 +44,76 @@ pub fn take_screenshot(display_id: u32, scale_factor: f32) -> anyhow::Result<Scr
bytes_per_row, bytes_per_row,
bytes, bytes,
scale_factor, scale_factor,
ScreenSamplePoints {
top: vec![],
bottom: vec![],
left: vec![],
right: vec![],
},
)) ))
} }
pub fn get_display_colors(
display_id: u32,
sample_points: &Vec<Vec<LedSamplePoints>>,
) -> anyhow::Result<Vec<LedColor>> {
log::debug!("take_screenshot");
let cg_display = CGDisplay::new(display_id);
let mut colors = vec![];
let start_at = std::time::Instant::now();
for points in sample_points {
if points.len() == 0 {
continue;
}
let start_x = points[0][0].0;
let start_y = points[0][0].1;
let end_x = points.last().unwrap().last().unwrap().0;
let end_y = points.last().unwrap().last().unwrap().1;
let (start_x, end_x) = (usize::min(start_x, end_x), usize::max(start_x, end_x));
let (start_y, end_y) = (usize::min(start_y, end_y), usize::max(start_y, end_y));
let origin = CGPoint {
x: start_x as f64 + cg_display.bounds().origin.x,
y: start_y as f64 + cg_display.bounds().origin.y,
};
let size = CGSize {
width: (end_x - start_x + 1) as f64,
height: (end_y - start_y + 1) as f64,
};
let cg_image = CGDisplay::screenshot(
CGRect::new(&origin, &size),
kCGWindowListOptionOnScreenOnly,
kCGNullWindowID,
kCGWindowImageDefault,
)
.ok_or_else(|| anyhow::anyhow!("Display#{}: take screenshot failed", display_id))?;
let bitmap = cg_image.data();
let points = points
.iter()
.map(|points| {
points
.iter()
.map(|(x, y)| (*x - start_x, *y - start_y))
.collect::<Vec<_>>()
})
.collect::<Vec<_>>();
let mut part_colors =
Screenshot::get_one_edge_colors_by_cg_image(&points, bitmap, cg_image.bytes_per_row());
colors.append(&mut part_colors);
}
// if display_id == 4849664 {
// log::info!(
// "======= get_display_colors {} took {}ms",
// display_id,
// start_at.elapsed().as_millis()
// );
// }
Ok(colors)
}
pub struct ScreenshotManager { pub struct ScreenshotManager {
pub channels: Arc<RwLock<HashMap<u32, watch::Receiver<Screenshot>>>>, pub channels: Arc<RwLock<HashMap<u32, watch::Receiver<Screenshot>>>>,
merged_screenshot_rx: Arc<RwLock<broadcast::Receiver<Screenshot>>>,
merged_screenshot_tx: Arc<RwLock<broadcast::Sender<Screenshot>>>, merged_screenshot_tx: Arc<RwLock<broadcast::Sender<Screenshot>>>,
} }
@ -66,10 +124,9 @@ impl ScreenshotManager {
SCREENSHOT_MANAGER SCREENSHOT_MANAGER
.get_or_init(|| async { .get_or_init(|| async {
let channels = Arc::new(RwLock::new(HashMap::new())); let channels = Arc::new(RwLock::new(HashMap::new()));
let (merged_screenshot_tx, merged_screenshot_rx) = broadcast::channel(2); let (merged_screenshot_tx, _) = broadcast::channel::<Screenshot>(2);
Self { Self {
channels, channels,
merged_screenshot_rx: Arc::new(RwLock::new(merged_screenshot_rx)),
merged_screenshot_tx: Arc::new(RwLock::new(merged_screenshot_tx)), merged_screenshot_tx: Arc::new(RwLock::new(merged_screenshot_tx)),
} }
}) })
@ -94,7 +151,8 @@ impl ScreenshotManager {
warn!("take_screenshot_loop: {}", screenshot.err().unwrap()); warn!("take_screenshot_loop: {}", screenshot.err().unwrap());
return; return;
} }
let mut interval = time::interval(Duration::from_millis(33)); let mut interval = time::interval(Duration::from_millis(3300));
let mut start = tokio::time::Instant::now();
let screenshot = screenshot.unwrap(); let screenshot = screenshot.unwrap();
let (screenshot_tx, screenshot_rx) = watch::channel(screenshot); let (screenshot_tx, screenshot_rx) = watch::channel(screenshot);
@ -107,7 +165,7 @@ impl ScreenshotManager {
let merged_screenshot_tx = merged_screenshot_tx.read().await.clone(); let merged_screenshot_tx = merged_screenshot_tx.read().await.clone();
loop { loop {
// interval.tick().await; start = tokio::time::Instant::now();
Self::take_screenshot_loop( Self::take_screenshot_loop(
display_id, display_id,
scale_factor, scale_factor,
@ -115,7 +173,8 @@ impl ScreenshotManager {
&merged_screenshot_tx, &merged_screenshot_tx,
) )
.await; .await;
tokio::time::sleep(Duration::from_millis(20)).await; interval.tick().await;
tokio::time::sleep(Duration::from_millis(1)).await;
} }
}); });
@ -130,9 +189,14 @@ impl ScreenshotManager {
) { ) {
let screenshot = take_screenshot(display_id, scale_factor); let screenshot = take_screenshot(display_id, scale_factor);
if let Ok(screenshot) = screenshot { if let Ok(screenshot) = screenshot {
screenshot_tx.send(screenshot.clone()).unwrap(); match merged_screenshot_tx.send(screenshot.clone()) {
merged_screenshot_tx.send(screenshot).unwrap(); Ok(_) => {}
log::debug!("take_screenshot_loop: send success. display#{}", display_id) Err(err) => {
// warn!("take_screenshot_loop: merged_screenshot_tx.send failed. display#{}. err: {}", display_id, err);
}
}
screenshot_tx.send(screenshot).unwrap();
// log::info!("take_screenshot_loop: send success. display#{}", display_id)
} else { } else {
warn!("take_screenshot_loop: {}", screenshot.err().unwrap()); warn!("take_screenshot_loop: {}", screenshot.err().unwrap());
} }
@ -155,10 +219,7 @@ impl ScreenshotManager {
all_colors all_colors
} }
pub async fn get_sorted_colors( pub fn get_sorted_colors(colors: &Vec<u8>, mappers: &Vec<SamplePointMapper>) -> Vec<u8> {
colors: &Vec<LedColor>,
mappers: &Vec<SamplePointMapper>,
) -> Vec<u8> {
let total_leds = mappers let total_leds = mappers
.iter() .iter()
.map(|mapper| usize::max(mapper.start, mapper.end)) .map(|mapper| usize::max(mapper.start, mapper.end))
@ -178,33 +239,29 @@ impl ScreenshotManager {
return; return;
} }
if color_index + group.start.abs_diff(group.end) > colors.len() { if color_index + group.start.abs_diff(group.end) * 3 > colors.len(){
warn!( warn!(
"get_sorted_colors: color_index out of range. color_index: {}, strip len: {}, colors.len(): {}", "get_sorted_colors: color_index out of range. color_index: {}, strip len: {}, colors.len(): {}",
color_index, color_index / 3,
group.start.abs_diff(group.end), group.start.abs_diff(group.end),
colors.len() colors.len() / 3
); );
return; return;
} }
if group.end > group.start { if group.end > group.start {
for i in group.start..group.end { for i in group.start..group.end {
let rgb = colors[color_index].get_rgb(); global_colors[i * 3] = colors[color_index +0];
color_index += 1; global_colors[i * 3 + 1] = colors[color_index +1];
global_colors[i * 3 + 2] = colors[color_index +2];
global_colors[i * 3] = rgb[0]; color_index += 3;
global_colors[i * 3 + 1] = rgb[1];
global_colors[i * 3 + 2] = rgb[2];
} }
} else { } else {
for i in (group.end..group.start).rev() { for i in (group.end..group.start).rev() {
let rgb = colors[color_index].get_rgb(); global_colors[i * 3] = colors[color_index +0];
color_index += 1; global_colors[i * 3 + 1] = colors[color_index +1];
global_colors[i * 3 + 2] = colors[color_index +2];
global_colors[i * 3] = rgb[0]; color_index += 3;
global_colors[i * 3 + 1] = rgb[1];
global_colors[i * 3 + 2] = rgb[2];
} }
} }
}); });

View File

@ -44,7 +44,7 @@ function App() {
// listen to led_colors_changed event // listen to led_colors_changed event
createEffect(() => { createEffect(() => {
const unlisten = listen<Array<string>>('led_colors_changed', (event) => { const unlisten = listen<Uint8ClampedArray>('led_colors_changed', (event) => {
const colors = event.payload; const colors = event.payload;
setLedStripStore({ setLedStripStore({

View File

@ -1,5 +1,4 @@
import { invoke } from '@tauri-apps/api'; import { invoke } from '@tauri-apps/api';
import { listen } from '@tauri-apps/api/event';
import { import {
Component, Component,
createEffect, createEffect,
@ -8,7 +7,6 @@ import {
createSignal, createSignal,
For, For,
JSX, JSX,
onCleanup,
splitProps, splitProps,
useContext, useContext,
} from 'solid-js'; } from 'solid-js';
@ -77,9 +75,15 @@ export const LedStripPart: Component<LedStripPartProps> = (props) => {
return; return;
} }
const offset = mapper.pos; const offset = mapper.pos * 3;
const colors = new Array(localProps.config.len).fill(null).map((_, i) => {
const index = offset + i * 3;
return `rgb(${ledStripStore.colors[index]}, ${ledStripStore.colors[index + 1]}, ${
ledStripStore.colors[index + 2]
})`;
});
const colors = ledStripStore.colors.slice(offset, offset + localProps.config.len);
setColors(colors); setColors(colors);
}); });

View File

@ -115,12 +115,21 @@ const SorterItem: Component<{ strip: LedStripConfig; mapper: LedStripPixelMapper
// update fullLeds // update fullLeds
createEffect(() => { createEffect(() => {
const fullLeds = new Array(ledStripStore.totalLedCount).fill(null); const fullLeds = new Array(ledStripStore.totalLedCount).fill(null);
const colors = ledStripStore.colors;
const { start, end, pos } = props.mapper; const { start, end, pos } = props.mapper;
const isForward = start < end; const isForward = start < end;
const step = isForward ? 1 : -1; const step = isForward ? 1 : -1;
for (let i = start, j = pos; i !== end; i += step, j++) { for (let i = start, j = pos; i !== end; i += step, j++) {
fullLeds[i] = ledStripStore.colors[j]; let c1 = `rgb(${Math.floor(colors[j * 3] * 0.8)}, ${Math.floor(
colors[j * 3 + 1] * 0.8,
)}, ${Math.floor(colors[j * 3 + 2] * 0.8)})`;
let c2 = `rgb(${Math.min(Math.floor(colors[j * 3] * 1.2), 255)}, ${Math.min(
Math.floor(colors[j * 3 + 1] * 1.2),
255,
)}, ${Math.min(Math.floor(colors[j * 3 + 2] * 1.2), 255)})`;
fullLeds[i] = `linear-gradient(70deg, ${c1} 10%, ${c2})`;
} }
setFullLeds(fullLeds); setFullLeds(fullLeds);

View File

@ -4,7 +4,7 @@ import { LedStripConfig, LedStripPixelMapper } from '../models/led-strip-config'
export const [ledStripStore, setLedStripStore] = createStore({ export const [ledStripStore, setLedStripStore] = createStore({
strips: new Array<LedStripConfig>(), strips: new Array<LedStripConfig>(),
mappers: new Array<LedStripPixelMapper>(), mappers: new Array<LedStripPixelMapper>(),
colors: new Array<string>(), colors: new Uint8ClampedArray(),
sortedColors: new Uint8ClampedArray(), sortedColors: new Uint8ClampedArray(),
get totalLedCount() { get totalLedCount() {
return Math.max(0, ...ledStripStore.mappers.map((m) => Math.max(m.start, m.end))); return Math.max(0, ...ledStripStore.mappers.map((m) => Math.max(m.start, m.end)));