#[cfg(not(feature = "lite"))]
extern crate rayon;
extern crate winapi;
#[cfg(not(feature = "lite"))]
use image::{GrayImage, ImageBuffer, ImageError, Luma, Rgba};
use std::mem::size_of;
use std::ptr::null_mut;
use winapi::shared::minwindef::{DWORD, HGLOBAL, LPVOID, UINT};
use winapi::um::wingdi::DIB_RGB_COLORS;
use winapi::um::wingdi::{
BitBlt, CreateCompatibleBitmap, CreateCompatibleDC, DeleteDC, DeleteObject, GetDIBits,
SelectObject, BITMAPINFO, BITMAPINFOHEADER, BI_RGB, RGBQUAD, SRCCOPY,
};
use winapi::um::winuser::{GetDC, ReleaseDC};
use crate::{imgtools, AutoGuiError};
#[derive(Debug, Clone)]
pub struct Screen {
pub screen_width: i32,
pub screen_height: i32,
#[cfg(not(feature = "lite"))]
pub screen_data: ScreenImgData,
}
#[derive(Debug, Clone)]
#[cfg(not(feature = "lite"))]
pub struct ScreenImgData {
pub screen_region_width: u32,
pub screen_region_height: u32,
pub pixel_data: Vec<u8>,
h_screen_dc: *mut winapi::shared::windef::HDC__,
h_memory_dc: *mut winapi::shared::windef::HDC__,
h_bitmap: *mut winapi::shared::windef::HBITMAP__,
}
impl Screen {
pub fn new() -> Result<Self, AutoGuiError> {
unsafe {
let screen_width: i32 = winapi::um::winuser::GetSystemMetrics(0);
let screen_height = winapi::um::winuser::GetSystemMetrics(1);
#[cfg(not(feature = "lite"))]
let screen_data = ScreenImgData {
screen_region_height: screen_height as u32,
screen_region_width: screen_width as u32,
pixel_data: vec![0u8; (screen_width * screen_height * 4) as usize],
h_screen_dc: GetDC(null_mut()),
h_memory_dc: CreateCompatibleDC(GetDC(null_mut())),
h_bitmap: CreateCompatibleBitmap(GetDC(null_mut()), screen_width, screen_height),
};
Ok(Screen {
screen_height,
screen_width,
#[cfg(not(feature = "lite"))]
screen_data,
})
}
}
pub fn dimension(&self) -> (i32, i32) {
(self.screen_width, self.screen_height)
}
#[cfg(not(feature = "lite"))]
#[allow(dead_code)]
pub fn region_dimension(&self) -> (u32, u32) {
(
self.screen_data.screen_region_width,
self.screen_data.screen_region_height,
)
}
#[cfg(not(feature = "lite"))]
pub fn destroy(&self) {
unsafe {
DeleteObject(self.screen_data.h_bitmap as HGLOBAL);
DeleteDC(self.screen_data.h_memory_dc);
ReleaseDC(null_mut(), self.screen_data.h_screen_dc);
}
}
#[cfg(not(feature = "lite"))]
#[allow(dead_code)]
pub fn grab_screen_image(
&mut self,
region: (u32, u32, u32, u32),
) -> Result<ImageBuffer<Rgba<u8>, Vec<u8>>, AutoGuiError> {
let (x, y, width, height) = region;
self.screen_data.screen_region_width = width;
self.screen_data.screen_region_height = height;
self.capture_screen();
let image = self.convert_bitmap_to_rgba()?;
let cropped_image: ImageBuffer<Rgba<u8>, Vec<u8>> =
imgtools::cut_screen_region(x, y, width, height, &image);
Ok(cropped_image)
}
#[cfg(not(feature = "lite"))]
pub fn grab_screen_image_grayscale(
&mut self,
region: &(u32, u32, u32, u32),
) -> Result<ImageBuffer<Luma<u8>, Vec<u8>>, AutoGuiError> {
let (x, y, width, height) = region;
self.screen_data.screen_region_width = *width;
self.screen_data.screen_region_height = *height;
self.capture_screen();
let image = self.convert_bitmap_to_grayscale()?;
let cropped_image: ImageBuffer<Luma<u8>, Vec<u8>> =
imgtools::cut_screen_region(*x, *y, *width, *height, &image);
Ok(cropped_image)
}
#[cfg(not(feature = "lite"))]
pub fn grab_screenshot(&mut self, image_path: &str) -> Result<(), AutoGuiError> {
self.capture_screen();
let image = self.convert_bitmap_to_rgba()?;
Ok(image.save(image_path)?)
}
#[cfg(not(feature = "lite"))]
fn capture_screen(&mut self) {
unsafe {
SelectObject(
self.screen_data.h_memory_dc,
self.screen_data.h_bitmap as HGLOBAL,
);
BitBlt(
self.screen_data.h_memory_dc,
0,
0,
self.screen_width,
self.screen_height,
self.screen_data.h_screen_dc,
0,
0,
SRCCOPY,
);
let mut bitmap_info = BITMAPINFO {
bmiHeader: BITMAPINFOHEADER {
biSize: size_of::<BITMAPINFOHEADER>() as DWORD,
biWidth: self.screen_width,
biHeight: -self.screen_height, biPlanes: 1,
biBitCount: 32,
biCompression: BI_RGB,
biSizeImage: 0,
biXPelsPerMeter: 0,
biYPelsPerMeter: 0,
biClrUsed: 0,
biClrImportant: 0,
},
bmiColors: [RGBQUAD {
rgbBlue: 0,
rgbGreen: 0,
rgbRed: 0,
rgbReserved: 0,
}; 1],
};
let mut bitmap_data: Vec<u8> =
vec![0u8; (self.screen_width * self.screen_height * 4) as usize];
GetDIBits(
self.screen_data.h_memory_dc,
self.screen_data.h_bitmap,
0,
self.screen_height as UINT,
bitmap_data.as_mut_ptr() as LPVOID,
&mut bitmap_info,
DIB_RGB_COLORS,
);
self.screen_data.pixel_data = bitmap_data
}
}
#[cfg(not(feature = "lite"))]
fn convert_bitmap_to_grayscale(&self) -> Result<ImageBuffer<Luma<u8>, Vec<u8>>, AutoGuiError> {
let mut grayscale_data =
Vec::with_capacity((self.screen_width * self.screen_height) as usize);
for chunk in self.screen_data.pixel_data.chunks_exact(4) {
let r = chunk[2] as u32;
let g = chunk[1] as u32;
let b = chunk[0] as u32;
let gray_value = ((r * 30 + g * 59 + b * 11) / 100) as u8;
grayscale_data.push(gray_value);
}
GrayImage::from_raw(
self.screen_width as u32,
self.screen_height as u32,
grayscale_data,
)
.ok_or(AutoGuiError::ImgError(
"could not convert image to grayscale".to_string(),
))
}
#[cfg(not(feature = "lite"))]
fn convert_bitmap_to_rgba(&self) -> Result<ImageBuffer<Rgba<u8>, Vec<u8>>, AutoGuiError> {
ImageBuffer::from_raw(
self.screen_width as u32,
self.screen_height as u32,
self.screen_data.pixel_data.clone(),
)
.ok_or(AutoGuiError::ImgError(
"failed to convert to RGBA".to_string(),
))
}
}