use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use std::time::Instant;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct Rect {
pub x: i32,
pub y: i32,
pub w: u32,
pub h: u32,
}
impl Rect {
pub const fn new(x: i32, y: i32, w: u32, h: u32) -> Self {
Self { x, y, w, h }
}
pub fn to_array(self) -> [i32; 4] {
[self.x, self.y, self.w as i32, self.h as i32]
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct WindowInfo {
pub hwnd: isize,
pub title: String,
pub exe: String,
pub class: String,
pub rect: Rect,
pub client_rect: Rect,
pub dpi: u32,
pub foreground: bool,
}
impl WindowInfo {
pub fn synthetic(title: impl Into<String>, w: u32, h: u32) -> Self {
Self {
hwnd: 0,
title: title.into(),
exe: "mock.exe".to_string(),
class: "Mock".to_string(),
rect: Rect::new(0, 0, w, h),
client_rect: Rect::new(0, 0, w, h),
dpi: 96,
foreground: true,
}
}
}
#[derive(Debug, Clone)]
pub struct RawFrame {
pub buffer: Arc<[u8]>,
pub width: u32,
pub height: u32,
pub stride: u32,
pub captured_at: Instant,
pub wall_time: DateTime<Utc>,
pub window: WindowInfo,
}
impl RawFrame {
pub fn from_bgra(
buffer: impl Into<Arc<[u8]>>,
width: u32,
height: u32,
captured_at: Instant,
wall_time: DateTime<Utc>,
window: WindowInfo,
) -> Self {
Self {
buffer: buffer.into(),
width,
height,
stride: width * 4,
captured_at,
wall_time,
window,
}
}
#[inline]
pub fn pixel(&self, x: u32, y: u32) -> (u8, u8, u8, u8) {
let off = (y * self.stride + x * 4) as usize;
let b = self.buffer.get(off).copied().unwrap_or(0);
let g = self.buffer.get(off + 1).copied().unwrap_or(0);
let r = self.buffer.get(off + 2).copied().unwrap_or(0);
let a = self.buffer.get(off + 3).copied().unwrap_or(255);
(b, g, r, a)
}
pub fn crop(&self, rect: Rect) -> RawFrame {
let fw = self.width as i64;
let fh = self.height as i64;
let x0 = (rect.x as i64).clamp(0, fw);
let y0 = (rect.y as i64).clamp(0, fh);
let x1 = (rect.x as i64 + rect.w as i64).clamp(0, fw);
let y1 = (rect.y as i64 + rect.h as i64).clamp(0, fh);
if x1 <= x0 || y1 <= y0 {
return self.clone();
}
let cw = (x1 - x0) as u32;
let ch = (y1 - y0) as u32;
let row_bytes = cw as usize * 4;
let mut buf = vec![0u8; row_bytes * ch as usize];
for dy in 0..ch as usize {
let sy = y0 as usize + dy;
let src = sy * self.stride as usize + x0 as usize * 4;
let dst = dy * row_bytes;
if src + row_bytes <= self.buffer.len() {
buf[dst..dst + row_bytes].copy_from_slice(&self.buffer[src..src + row_bytes]);
}
}
RawFrame {
buffer: Arc::from(buf.into_boxed_slice()),
width: cw,
height: ch,
stride: cw * 4,
captured_at: self.captured_at,
wall_time: self.wall_time,
window: self.window.clone(),
}
}
}