Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: 修改代码风格,优化截图方法 #94

Merged
merged 3 commits into from
Jan 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "xcap"
version = "0.0.1"
version = "0.0.2"
edition = "2021"
description = "A cross-platform screen capture library"
license = "Apache-2.0"
Expand Down
2 changes: 1 addition & 1 deletion examples/monitor_capture.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ fn main() {
let image = monitor.capture_image().unwrap();

image
.save(format!("monitor-{}.png", normalized(monitor.name())))
.save(format!("target/monitor-{}.png", normalized(monitor.name())))
.unwrap();
}

Expand Down
4 changes: 2 additions & 2 deletions src/utils/image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,10 @@ mod tests {

#[test]
fn bgra() {
let image = bgra_to_rgba_image(2, 1, vec![1, 2, 3, 4, 255, 254, 253, 252]).unwrap();
let image = bgra_to_rgba_image(2, 1, vec![1, 2, 3, 255, 255, 254, 253, 255]).unwrap();
assert_eq!(
image,
RgbaImage::from_vec(2, 1, vec![3, 2, 1, 4, 253, 254, 255, 252]).unwrap()
RgbaImage::from_vec(2, 1, vec![3, 2, 1, 255, 253, 254, 255, 255]).unwrap()
);
}

Expand Down
84 changes: 27 additions & 57 deletions src/windows/boxed.rs
Original file line number Diff line number Diff line change
@@ -1,35 +1,41 @@
use std::{ops::Deref, ptr};
use windows::{
core::PCWSTR,
Win32::Graphics::Gdi::{CreateDCW, DeleteDC, DeleteObject, GetWindowDC, HBITMAP, HDC},
Win32::{
Foundation::HWND,
Graphics::Gdi::{CreateDCW, DeleteDC, DeleteObject, GetWindowDC, ReleaseDC, HBITMAP, HDC},
},
};
use windows::Win32::Foundation::HWND;
use windows::Win32::Graphics::Gdi::{GetDC, ReleaseDC};
use windows::Win32::UI::WindowsAndMessaging::GetDesktopWindow;
use crate::{XCapError, XCapResult};

use super::{impl_monitor::ImplMonitor, impl_window::ImplWindow};

pub(crate) struct BoxHDC(HDC);
pub(super) struct BoxHDC {
hdc: HDC,
hwnd: Option<HWND>,
}

impl Deref for BoxHDC {
type Target = HDC;
fn deref(&self) -> &Self::Target {
&self.0
&self.hdc
}
}

impl Drop for BoxHDC {
fn drop(&mut self) {
// ReleaseDC 与 DeleteDC 的区别
// https://learn.microsoft.com/zh-cn/windows/win32/api/winuser/nf-winuser-releasedc
unsafe {
DeleteDC(self.0);
if let Some(hwnd) = self.hwnd {
ReleaseDC(hwnd, self.hdc);
} else {
DeleteDC(self.hdc);
}
};
}
}

impl BoxHDC {
pub fn new(hdc: HDC) -> Self {
BoxHDC(hdc)
pub fn new(hdc: HDC, hwnd: Option<HWND>) -> Self {
BoxHDC { hdc, hwnd }
}
}

Expand All @@ -46,25 +52,21 @@ impl From<&[u16; 32]> for BoxHDC {
)
};

BoxHDC::new(hdc)
}
}

impl From<&ImplMonitor> for BoxHDC {
fn from(impl_monitor: &ImplMonitor) -> Self {
BoxHDC::from(&impl_monitor.monitor_info_ex_w.szDevice)
BoxHDC::new(hdc, None)
}
}

impl From<&ImplWindow> for BoxHDC {
fn from(impl_window: &ImplWindow) -> Self {
let hdc = unsafe { GetWindowDC(impl_window.hwnd) };
impl From<HWND> for BoxHDC {
fn from(hwnd: HWND) -> Self {
// GetWindowDC vs GetDC, GetDC 不会绘制窗口边框
// https://learn.microsoft.com/zh-cn/windows/win32/api/winuser/nf-winuser-getwindowdc
let hdc = unsafe { GetWindowDC(hwnd) };

BoxHDC::new(hdc)
BoxHDC::new(hdc, Some(hwnd))
}
}

pub(crate) struct BoxHBITMAP(HBITMAP);
pub(super) struct BoxHBITMAP(HBITMAP);

impl Deref for BoxHBITMAP {
type Target = HBITMAP;
Expand All @@ -75,6 +77,7 @@ impl Deref for BoxHBITMAP {

impl Drop for BoxHBITMAP {
fn drop(&mut self) {
// https://learn.microsoft.com/zh-cn/windows/win32/api/wingdi/nf-wingdi-createcompatiblebitmap
unsafe {
DeleteObject(self.0);
};
Expand All @@ -86,36 +89,3 @@ impl BoxHBITMAP {
BoxHBITMAP(h_bitmap)
}
}

pub(crate) struct BoxMonitorHDC {
hwnd: HWND,
hdc: HDC,
}
impl Deref for BoxMonitorHDC {
type Target = HDC;
fn deref(&self) -> &Self::Target {
&self.hdc
}
}

impl Drop for BoxMonitorHDC {
fn drop(&mut self) {
unsafe {
ReleaseDC(self.hwnd, self.hdc);
};
}
}

impl BoxMonitorHDC {
pub unsafe fn new() -> XCapResult<BoxMonitorHDC> {
let hwnd = GetDesktopWindow();
let hdc = GetDC(hwnd);
if hdc.is_invalid() {
return Err(XCapError::new("GetDC is failed"))
}
Ok(BoxMonitorHDC{
hwnd,
hdc,
})
}
}
49 changes: 23 additions & 26 deletions src/windows/capture.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,21 @@
use image::RgbaImage;
use std::mem;
use windows::Win32::{
Foundation::HWND,
Graphics::Gdi::{
BitBlt, CreateCompatibleBitmap, CreateCompatibleDC, GetDIBits, SelectObject, BITMAPINFO,
BITMAPINFOHEADER, DIB_RGB_COLORS, RGBQUAD, SRCCOPY,
},
Storage::Xps::{PrintWindow, PRINT_WINDOW_FLAGS, PW_CLIENTONLY},
UI::WindowsAndMessaging::PW_RENDERFULLCONTENT,
UI::WindowsAndMessaging::{GetDesktopWindow, PW_RENDERFULLCONTENT},
};

use crate::{
error::{XCapError, XCapResult},
utils::image::bgra_to_rgba_image,
};
use crate::platform::boxed::BoxMonitorHDC;

use super::{
boxed::{BoxHBITMAP, BoxHDC},
impl_monitor::ImplMonitor,
impl_window::ImplWindow,
};
use super::boxed::{BoxHBITMAP, BoxHDC};

fn to_rgba_image(
box_hdc_mem: BoxHDC,
Expand Down Expand Up @@ -68,33 +64,33 @@ fn to_rgba_image(
}

#[allow(unused)]
pub fn capture_monitor(
impl_monitor: &ImplMonitor,
x: i32,
y: i32,
width: i32,
height: i32,
) -> XCapResult<RgbaImage> {
pub fn capture_monitor(x: i32, y: i32, width: i32, height: i32) -> XCapResult<RgbaImage> {
unsafe {
let box_hdc_monitor = BoxMonitorHDC::new()?;

// 内存中的HDC
let box_hdc_mem = BoxHDC::new(CreateCompatibleDC(*box_hdc_monitor));
let box_h_bitmap = BoxHBITMAP::new(CreateCompatibleBitmap(*box_hdc_monitor, width, height));
let hwnd = GetDesktopWindow();
let box_hdc_desktop_window = BoxHDC::from(hwnd);

// 内存中的HDC,使用 DeleteDC 函数释放
// https://learn.microsoft.com/zh-cn/windows/win32/api/wingdi/nf-wingdi-createcompatibledc
let box_hdc_mem = BoxHDC::new(CreateCompatibleDC(*box_hdc_desktop_window), None);
let box_h_bitmap = BoxHBITMAP::new(CreateCompatibleBitmap(
*box_hdc_desktop_window,
width,
height,
));

// 使用SelectObject函数将这个位图选择到DC中
SelectObject(*box_hdc_mem, *box_h_bitmap);

// 拷贝原始图像到内存
// 咋合理不需要i缩放图片,所以直接使用BitBlt
// 这里不需要缩放图片,所以直接使用BitBlt
// 如需要缩放,则使用 StretchBlt
BitBlt(
*box_hdc_mem,
0,
0,
width,
height,
*box_hdc_monitor,
*box_hdc_desktop_window,
x,
y,
SRCCOPY,
Expand All @@ -105,11 +101,12 @@ pub fn capture_monitor(
}

#[allow(unused)]
pub fn capture_window(impl_window: &ImplWindow, width: i32, height: i32) -> XCapResult<RgbaImage> {
pub fn capture_window(hwnd: HWND, width: i32, height: i32) -> XCapResult<RgbaImage> {
unsafe {
let box_hdc_window: BoxHDC = BoxHDC::from(impl_window);
// 内存中的HDC
let box_hdc_mem = BoxHDC::new(CreateCompatibleDC(*box_hdc_window));
let box_hdc_window: BoxHDC = BoxHDC::from(hwnd);
// 内存中的HDC,使用 DeleteDC 函数释放
// https://learn.microsoft.com/zh-cn/windows/win32/api/wingdi/nf-wingdi-createcompatibledc
let box_hdc_mem = BoxHDC::new(CreateCompatibleDC(*box_hdc_window), None);
let box_h_bitmap = BoxHBITMAP::new(CreateCompatibleBitmap(*box_hdc_window, width, height));

// 使用SelectObject函数将这个位图选择到DC中
Expand All @@ -121,7 +118,7 @@ pub fn capture_window(impl_window: &ImplWindow, width: i32, height: i32) -> XCap
// the window that are drawn using DirectComposition.
// https://github.com/chromium/chromium/blob/main/ui/snapshot/snapshot_win.cc#L39-L45
let flags = PW_CLIENTONLY.0 | PW_RENDERFULLCONTENT;
PrintWindow(impl_window.hwnd, *box_hdc_mem, PRINT_WINDOW_FLAGS(flags));
PrintWindow(hwnd, *box_hdc_mem, PRINT_WINDOW_FLAGS(flags));

to_rgba_image(box_hdc_mem, box_h_bitmap, width, height)
}
Expand Down
56 changes: 19 additions & 37 deletions src/windows/impl_monitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,27 @@ use windows::{
Win32::{
Foundation::{BOOL, LPARAM, POINT, RECT, TRUE},
Graphics::Gdi::{
EnumDisplayMonitors, GetDeviceCaps, GetMonitorInfoW,
MonitorFromPoint, DESKTOPHORZRES, DEVMODEW, DEVMODE_DISPLAY_ORIENTATION,
EnumDisplayMonitors, EnumDisplaySettingsW, GetDeviceCaps, GetMonitorInfoW,
MonitorFromPoint, DESKTOPHORZRES, DEVMODEW, DMDO_180, DMDO_270, DMDO_90, DMDO_DEFAULT,
ENUM_CURRENT_SETTINGS, HDC, HMONITOR, HORZRES, MONITORINFO, MONITORINFOEXW,
MONITOR_DEFAULTTONULL,
},
UI::WindowsAndMessaging::MONITORINFOF_PRIMARY,
},
};
use windows::Win32::Graphics::Gdi::EnumDisplaySettingsW;

use crate::error::{XCapError, XCapResult};

use super::{boxed::BoxHDC, capture::capture_monitor, utils::wide_string_to_string};

// A函数与W函数区别
// A 函数与 W 函数区别
// https://learn.microsoft.com/zh-cn/windows/win32/learnwin32/working-with-strings

#[derive(Debug, Clone)]
pub(crate) struct ImplMonitor {
#[allow(unused)]
pub hmonitor: HMONITOR,
#[allow(unused)]
pub monitor_info_ex_w: MONITORINFOEXW,
pub id: u32,
pub name: String,
Expand Down Expand Up @@ -59,13 +59,7 @@ fn get_dev_mode_w(monitor_info_exw: &MONITORINFOEXW) -> XCapResult<DEVMODEW> {
dev_mode_w.dmSize = mem::size_of::<DEVMODEW>() as u16;

unsafe {
EnumDisplaySettingsW(
PCWSTR(sz_device),
ENUM_CURRENT_SETTINGS,
&mut dev_mode_w,
// EDS_RAWMODE,
)
.ok()?;
EnumDisplaySettingsW(PCWSTR(sz_device), ENUM_CURRENT_SETTINGS, &mut dev_mode_w).ok()?;
};

Ok(dev_mode_w)
Expand All @@ -80,23 +74,23 @@ impl ImplMonitor {

// https://learn.microsoft.com/zh-cn/windows/win32/api/winuser/nf-winuser-getmonitorinfoa
unsafe { GetMonitorInfoW(hmonitor, monitor_info_ex_w_ptr).ok()? };
let mut rc_monitor = monitor_info_ex_w.monitorInfo.rcMonitor;

let dev_mode_w = get_dev_mode_w(&monitor_info_ex_w)?;

let dm_position = unsafe { dev_mode_w.Anonymous1.Anonymous2.dmPosition };
let dm_pels_width = dev_mode_w.dmPelsWidth;
let dm_pels_height = dev_mode_w.dmPelsHeight;

let dm_display_orientation =
unsafe { dev_mode_w.Anonymous1.Anonymous2.dmDisplayOrientation };

let rotation = match dm_display_orientation {
DEVMODE_DISPLAY_ORIENTATION(0) => 0.0,
DEVMODE_DISPLAY_ORIENTATION(1) => 90.0,
DEVMODE_DISPLAY_ORIENTATION(2) => 180.0,
DEVMODE_DISPLAY_ORIENTATION(3) => 270.0,
_ => dm_display_orientation.0 as f32,
DMDO_90 => 90.0,
DMDO_180 => 180.0,
DMDO_270 => 270.0,
DMDO_DEFAULT => 0.0,
_ => 0.0,
};

let dev_mode_w = get_dev_mode_w(&monitor_info_ex_w)?;

let box_hdc_monitor = BoxHDC::from(&monitor_info_ex_w.szDevice);

let scale_factor = unsafe {
Expand All @@ -106,23 +100,15 @@ impl ImplMonitor {
physical_width as f32 / logical_width as f32
};

unsafe {
rc_monitor.left = dev_mode_w.Anonymous1.Anonymous2.dmPosition.x;
rc_monitor.right = dev_mode_w.Anonymous1.Anonymous2.dmPosition.x + dev_mode_w.dmPelsWidth as i32;
rc_monitor.top = dev_mode_w.Anonymous1.Anonymous2.dmPosition.y;
rc_monitor.bottom = dev_mode_w.Anonymous1.Anonymous2.dmPosition.y + dev_mode_w.dmPelsHeight as i32;

}

Ok(ImplMonitor {
hmonitor,
monitor_info_ex_w,
id: hmonitor.0 as u32,
name: wide_string_to_string(&monitor_info_ex_w.szDevice)?,
x: rc_monitor.left,
y: rc_monitor.top,
width: (rc_monitor.right - rc_monitor.left) as u32,
height: (rc_monitor.bottom - rc_monitor.top) as u32,
x: dm_position.x,
y: dm_position.y,
width: dm_pels_width,
height: dm_pels_height,
rotation,
scale_factor,
frequency: dev_mode_w.dmDisplayFrequency as f32,
Expand Down Expand Up @@ -167,10 +153,6 @@ impl ImplMonitor {

impl ImplMonitor {
pub fn capture_image(&self) -> XCapResult<RgbaImage> {
// let width = ((self.width as f32) * self.scale_factor) as i32;
// let height = ((self.height as f32) * self.scale_factor) as i32;
// let x = ((self.x as f32) * self.scale_factor) as i32;
// let y = ((self.y as f32) * self.scale_factor) as i32;
capture_monitor(self,self.x,self.y, self.width as i32, self.height as i32)
capture_monitor(self.x, self.y, self.width as i32, self.height as i32)
}
}
2 changes: 1 addition & 1 deletion src/windows/impl_window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,6 @@ impl ImplWindow {
let width = ((self.width as f32) * self.current_monitor.scale_factor) as i32;
let height = ((self.height as f32) * self.current_monitor.scale_factor) as i32;

capture_window(self, width, height)
capture_window(self.hwnd, width, height)
}
}
Loading