feat: 更改截图逻辑。

This commit is contained in:
Ivan Li 2023-03-19 10:17:13 +08:00
parent 653729fcc2
commit 596da96f8c
5 changed files with 113 additions and 15 deletions

36
src-tauri/Cargo.lock generated
View File

@ -856,7 +856,9 @@ dependencies = [
"base64 0.13.1", "base64 0.13.1",
"bmp", "bmp",
"color_space", "color_space",
"core-graphics",
"ddc-hi", "ddc-hi",
"display-info",
"either", "either",
"futures", "futures",
"hex", "hex",
@ -880,6 +882,20 @@ dependencies = [
"webp", "webp",
] ]
[[package]]
name = "display-info"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15453e90755c09fc70a6dbc9a307b0d4ec0fa65c5e36fb7cf8246109c442c331"
dependencies = [
"anyhow",
"core-graphics",
"fxhash",
"widestring",
"windows 0.44.0",
"xcb",
]
[[package]] [[package]]
name = "dns-parser" name = "dns-parser"
version = "0.8.0" version = "0.8.0"
@ -2804,6 +2820,15 @@ version = "1.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
[[package]]
name = "quick-xml"
version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8533f14c8382aaad0d592c812ac3b826162128b65662331e1127b45c3d18536b"
dependencies = [
"memchr 2.5.0",
]
[[package]] [[package]]
name = "quick-xml" name = "quick-xml"
version = "0.23.1" version = "0.23.1"
@ -4709,6 +4734,17 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "xcb"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0faeb4d7e2d54fff4a0584f61297e86b106914af2029778de7b427f72564d6c5"
dependencies = [
"bitflags",
"libc",
"quick-xml 0.22.0",
]
[[package]] [[package]]
name = "yaml-rust" name = "yaml-rust"
version = "0.4.5" version = "0.4.5"

View File

@ -38,6 +38,8 @@ macos-app-nap = "0.0.1"
ddc-hi = "0.4.1" ddc-hi = "0.4.1"
redb = "0.13.0" redb = "0.13.0"
paho-mqtt = "0.12.0" paho-mqtt = "0.12.0"
core-graphics = "0.22.3"
display-info = "0.4.1"
[features] [features]
# by default Tauri runs in production mode # by default Tauri runs in production mode

View File

@ -17,11 +17,12 @@ pub struct LedStripConfig {
#[derive(Clone, Copy, Serialize, Deserialize, Debug)] #[derive(Clone, Copy, Serialize, Deserialize, Debug)]
pub struct DisplayConfig { pub struct DisplayConfig {
pub id: usize, pub id: u32,
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 led_strip_of_borders: LedStripConfigOfBorders, pub led_strip_of_borders: LedStripConfigOfBorders,
pub scale_factor: f32,
} }
impl LedStripConfigOfBorders { impl LedStripConfigOfBorders {
@ -37,10 +38,11 @@ impl LedStripConfigOfBorders {
impl DisplayConfig { impl DisplayConfig {
pub fn default( pub fn default(
id: usize, id: u32,
index_of_display: usize, index_of_display: usize,
display_width: usize, display_width: usize,
display_height: usize, display_height: usize,
scale_factor: f32,
) -> Self { ) -> Self {
Self { Self {
id, id,
@ -48,6 +50,7 @@ impl DisplayConfig {
display_width, display_width,
display_height, display_height,
led_strip_of_borders: LedStripConfigOfBorders::default(), led_strip_of_borders: LedStripConfigOfBorders::default(),
scale_factor,
} }
} }
} }

View File

@ -1,5 +1,9 @@
use core_graphics::display::{
kCGNullWindowID, kCGWindowImageDefault, kCGWindowListOptionOnScreenOnly, CGDisplay,
};
use paris::info; use paris::info;
use scrap::{Capturer, Display}; use scrap::{Capturer, Display};
use tracing::debug;
use super::{config::DisplayConfig, screen::Screen, screenshot::Screenshot}; use super::{config::DisplayConfig, screen::Screen, screenshot::Screenshot};
@ -12,10 +16,7 @@ impl DisplayPicker {
pub fn from_config(config: DisplayConfig) -> anyhow::Result<Self> { pub fn from_config(config: DisplayConfig) -> anyhow::Result<Self> {
let displays = Display::all() let displays = Display::all()
.map_err(|error| anyhow::anyhow!("Can not get all of displays. {}", error))?; .map_err(|error| anyhow::anyhow!("Can not get all of displays. {}", error))?;
let display = displays let display = displays.into_iter().skip(config.index_of_display).next();
.into_iter()
.skip(config.index_of_display)
.next();
match display { match display {
Some(display) => { Some(display) => {
@ -35,13 +36,67 @@ impl DisplayPicker {
} }
pub fn take_screenshot(&mut self) -> anyhow::Result<Screenshot> { pub fn take_screenshot(&mut self) -> anyhow::Result<Screenshot> {
let bitmap = self debug!("take_screenshot");
.screen let start_at = std::time::Instant::now();
.take()
.map_err(|error| anyhow::anyhow!("take screenshot for display failed. {}", error))?; let cg_display = CGDisplay::new(self.config.id);
let cg_image = CGDisplay::screenshot(
cg_display.bounds(),
kCGWindowListOptionOnScreenOnly,
kCGNullWindowID,
kCGWindowImageDefault,
)
.ok_or_else(|| anyhow::anyhow!("Display#{}: take screenshot failed", self.config.id))?;
debug!("take screenshot took {}ms", start_at.elapsed().as_millis());
let buffer = cg_image.data();
let bytes_per_row = cg_image.bytes_per_row() as f32;
let height = cg_image.height();
let width = cg_image.width();
let scale_factor = self.config.scale_factor;
let image_height = (height as f32 / scale_factor) as u32;
let image_width = (width as f32 / scale_factor) as u32;
debug!(
"raw image: {}x{}, output image: {}x{}",
width, height, image_width, image_height
);
// // from bitmap vec
let mut image_buffer = vec![0u8; (image_width * image_height * 3) as usize];
for y in 0..image_height {
for x in 0..image_width {
let offset =
(((y as f32) * bytes_per_row + (x as f32) * 4.0) * scale_factor) as usize;
let b = buffer[offset];
let g = buffer[offset + 1];
let r = buffer[offset + 2];
let a = buffer[offset + 3];
let offset = (y * image_width + x) as usize;
image_buffer[offset * 3] = r;
image_buffer[offset * 3 + 1] = g;
image_buffer[offset * 3 + 2] = b;
}
}
debug!(
"convert to image buffer took {}ms",
start_at.elapsed().as_millis()
);
// println!("encode to png took {}ms", start_at.elapsed().as_millis());
// // // base64 image
// let mut image_base64 = String::new();
// image_base64.push_str("data:image/png;base64,");
// let encoded = base64::engine::general_purpose::STANDARD_NO_PAD.encode(image_png);
// image_base64.push_str(encoded.as_str());
// println!("took {}ms", start_at.elapsed().as_millis());
// println!("image_base64: {}", image_base64.len());
// info!("bitmap size {}", bitmap.len()); // info!("bitmap size {}", bitmap.len());
let screenshot = Screenshot::new(bitmap, self.config); let screenshot = Screenshot::new(image_buffer, self.config);
Ok(screenshot) Ok(screenshot)
} }
} }

View File

@ -1,3 +1,5 @@
use core_graphics::display::CGDisplay;
use display_info::DisplayInfo;
use futures::{stream::FuturesUnordered, StreamExt}; use futures::{stream::FuturesUnordered, StreamExt};
use paris::info; use paris::info;
use scrap::Display; use scrap::Display;
@ -43,16 +45,16 @@ impl Picker {
pub async fn list_displays(&self) -> anyhow::Result<Vec<ScreenshotDto>> { pub async fn list_displays(&self) -> anyhow::Result<Vec<ScreenshotDto>> {
let mut configs = vec![]; let mut configs = vec![];
let displays = Display::all() let displays = DisplayInfo::all()
.map_err(|error| anyhow::anyhow!("Can not get all of displays. {}", error))?; .map_err(|error| anyhow::anyhow!("Can not get all of displays. {}", error))?;
// configs.clear(); // configs.clear();
let mut futs = FuturesUnordered::new(); let mut futs = FuturesUnordered::new();
for (index, display) in displays.iter().enumerate() { for (index, display) in displays.iter().enumerate() {
let height = display.height(); let height = display.height as usize;
let width = display.width(); let width = display.width as usize;
let config = DisplayConfig::default(index, index, width, height); let config = DisplayConfig::default(display.id, index, width, height, display.scale_factor);
configs.push(config); configs.push(config);
} }