diff --git a/Cargo.lock b/Cargo.lock index 108addb..bc62cf1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -235,16 +235,6 @@ version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" -[[package]] -name = "matrixmultiply" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7574c1cf36da4798ab73da5b215bbf444f50718207754cb522201d78d1cd0ff2" -dependencies = [ - "autocfg", - "rawpointer", -] - [[package]] name = "memoffset" version = "0.9.0" @@ -264,28 +254,6 @@ dependencies = [ "simd-adler32", ] -[[package]] -name = "ndarray" -version = "0.15.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adb12d4e967ec485a5f71c6311fe28158e9d6f4bc4a447b474184d0f91a8fa32" -dependencies = [ - "matrixmultiply", - "num-complex", - "num-integer", - "num-traits", - "rawpointer", -] - -[[package]] -name = "num-complex" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214" -dependencies = [ - "num-traits", -] - [[package]] name = "num-integer" version = "0.1.45" @@ -446,12 +414,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "rawpointer" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" - [[package]] name = "rayon" version = "1.8.0" @@ -586,11 +548,10 @@ dependencies = [ [[package]] name = "windows-capture" -version = "1.0.29" +version = "1.0.32" dependencies = [ "image", "log", - "ndarray", "parking_lot", "thiserror", "windows", @@ -598,7 +559,7 @@ dependencies = [ [[package]] name = "windows-capture-python" -version = "1.0.29" +version = "1.0.32" dependencies = [ "pyo3", "windows", diff --git a/Cargo.toml b/Cargo.toml index 3facb8a..66e33c4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "windows-capture" -version = "1.0.29" +version = "1.0.32" authors = ["NiiightmareXD"] edition = "2021" description = "Fastest Windows Screen Capture Library For Rust 🔥" @@ -21,7 +21,6 @@ resolver = "2" [dependencies] image = "0.24.7" log = "0.4.20" -ndarray = "0.15.6" parking_lot = "0.12.1" thiserror = "1.0.50" windows = { version = "0.52.0", features = [ diff --git a/README.md b/README.md index e5cfd61..07c7e18 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ Add this library to your `Cargo.toml`: ```toml [dependencies] -windows-capture = "1.0.29" +windows-capture = "1.0.32" ``` or run this command @@ -56,7 +56,7 @@ impl WindowsCaptureHandler for Capture { // Called Every Time A New Frame Is Available fn on_frame_arrived( &mut self, - mut frame: Frame, + frame: &mut Frame, capture_control: InternalCaptureControl, ) -> Result<(), Box> { println!("New Frame Arrived"); diff --git a/examples/basic.rs b/examples/basic.rs index 2bdae30..c23c3c8 100644 --- a/examples/basic.rs +++ b/examples/basic.rs @@ -25,7 +25,7 @@ impl WindowsCaptureHandler for Capture { // Called Every Time A New Frame Is Available fn on_frame_arrived( &mut self, - mut frame: Frame, + frame: &mut Frame, capture_control: InternalCaptureControl, ) -> Result<(), Box> { println!("New Frame Arrived"); diff --git a/src/capture.rs b/src/capture.rs index ace1629..e1130ff 100644 --- a/src/capture.rs +++ b/src/capture.rs @@ -321,7 +321,7 @@ pub trait WindowsCaptureHandler: Sized { /// Called Every Time A New Frame Is Available fn on_frame_arrived( &mut self, - frame: Frame, + frame: &mut Frame, capture_control: InternalCaptureControl, ) -> Result<(), Box>; diff --git a/src/frame.rs b/src/frame.rs index ccdb1b0..0b63005 100644 --- a/src/frame.rs +++ b/src/frame.rs @@ -1,7 +1,7 @@ -use std::{error::Error, path::Path, slice}; +use std::{error::Error, path::Path, ptr, slice}; -use image::{Rgba, RgbaImage}; -use ndarray::{s, ArrayBase, ArrayView, Dim, OwnedRepr}; +use image::{Rgb, RgbImage}; +use log::trace; use windows::Win32::Graphics::{ Direct3D11::{ ID3D11Device, ID3D11DeviceContext, ID3D11Texture2D, D3D11_BOX, D3D11_CPU_ACCESS_READ, @@ -24,6 +24,7 @@ pub struct Frame<'a> { d3d_device: &'a ID3D11Device, frame_surface: ID3D11Texture2D, context: &'a ID3D11DeviceContext, + buffer: &'a mut Vec, width: u32, height: u32, color_format: ColorFormat, @@ -32,10 +33,11 @@ pub struct Frame<'a> { impl<'a> Frame<'a> { /// Craete A New Frame #[must_use] - pub const fn new( + pub fn new( d3d_device: &'a ID3D11Device, frame_surface: ID3D11Texture2D, context: &'a ID3D11DeviceContext, + buffer: &'a mut Vec, width: u32, height: u32, color_format: ColorFormat, @@ -44,6 +46,7 @@ impl<'a> Frame<'a> { d3d_device, frame_surface, context, + buffer, width, height, color_format, @@ -116,8 +119,11 @@ impl<'a> Frame<'a> { // Create Frame Buffer From Slice let frame_buffer = FrameBuffer::new( mapped_frame_data, + self.buffer, self.width, self.height, + mapped_resource.RowPitch, + mapped_resource.DepthPitch, self.color_format, ); @@ -210,51 +216,25 @@ impl<'a> Frame<'a> { // Create Frame Buffer From Slice let frame_buffer = FrameBuffer::new( mapped_frame_data, + self.buffer, texture_width, texture_height, + mapped_resource.RowPitch, + mapped_resource.DepthPitch, self.color_format, ); Ok(frame_buffer) } - /// Save The Frame As An Image To The Specified Path + /// Save The Frame Buffer As An Image To The Specified Path pub fn save_as_image>( &mut self, path: T, ) -> Result<(), Box> { - let buffer = self.buffer()?; - - let nopadding_buffer = buffer.as_raw_nopadding_buffer()?; - - let (height, width, _) = nopadding_buffer.dim(); - let mut rgba_image: RgbaImage = RgbaImage::new(width as u32, height as u32); - - if self.color_format == ColorFormat::Rgba8 { - for y in 0..height { - for x in 0..width { - let r = nopadding_buffer[(y, x, 0)]; - let g = nopadding_buffer[(y, x, 1)]; - let b = nopadding_buffer[(y, x, 2)]; - let a = nopadding_buffer[(y, x, 3)]; - - rgba_image.put_pixel(x as u32, y as u32, Rgba([r, g, b, a])); - } - } - } else { - for y in 0..height { - for x in 0..width { - let b = nopadding_buffer[(y, x, 0)]; - let g = nopadding_buffer[(y, x, 1)]; - let r = nopadding_buffer[(y, x, 2)]; - let a = nopadding_buffer[(y, x, 3)]; - - rgba_image.put_pixel(x as u32, y as u32, Rgba([r, g, b, a])); - } - } - } + let frame_buffer = self.buffer()?; - rgba_image.save(path)?; + frame_buffer.save_as_image(path)?; Ok(()) } @@ -263,24 +243,33 @@ impl<'a> Frame<'a> { /// Frame Buffer Struct Used To Get Raw Pixel Data pub struct FrameBuffer<'a> { raw_buffer: &'a [u8], + buffer: &'a mut Vec, width: u32, height: u32, + row_pitch: u32, + depth_pitch: u32, color_format: ColorFormat, } impl<'a> FrameBuffer<'a> { /// Create A New Frame Buffer #[must_use] - pub const fn new( + pub fn new( raw_buffer: &'a [u8], + buffer: &'a mut Vec, width: u32, height: u32, + row_pitch: u32, + depth_pitch: u32, color_format: ColorFormat, ) -> Self { Self { raw_buffer, + buffer, width, height, + row_pitch, + depth_pitch, color_format, } } @@ -297,12 +286,22 @@ impl<'a> FrameBuffer<'a> { self.height } - /// Get The Frame Buffer Height + /// Get The Frame Buffer Row Pitch #[must_use] - pub const fn has_padding(&self) -> bool { - let raw_buffer = self.as_raw_buffer(); + pub const fn row_pitch(&self) -> u32 { + self.row_pitch + } - self.width as usize * 4 != raw_buffer.len() / self.height as usize + /// Get The Frame Buffer Depth Pitch + #[must_use] + pub const fn depth_pitch(&self) -> u32 { + self.depth_pitch + } + + /// Check If The Buffer Has Padding + #[must_use] + pub const fn has_padding(&self) -> bool { + self.width * 4 != self.row_pitch } /// Get The Raw Pixel Data Might Have Padding @@ -314,24 +313,26 @@ impl<'a> FrameBuffer<'a> { /// Get The Raw Pixel Data Without Padding #[allow(clippy::type_complexity)] pub fn as_raw_nopadding_buffer( - &self, - ) -> Result, Dim<[usize; 3]>>, Box> { - let row_pitch = self.raw_buffer.len() / self.height as usize; - - let array = - ArrayView::from_shape((self.height as usize, row_pitch), self.raw_buffer)?.to_owned(); - - if self.width as usize * 4 == self.raw_buffer.len() / self.height as usize { - let array = array.into_shape((self.height as usize, self.width as usize, 4))?; + &'a mut self, + ) -> Result<&'a mut Vec, Box> { + if self.buffer.capacity() < (self.width * self.height) as usize { + trace!("Resizing Preallocated Buffer"); + self.buffer.resize((self.width * self.height) as usize, 0); + } - Ok(array) - } else { - let array = array - .slice_move(s![.., ..self.width as usize * 4]) - .into_shape((self.height as usize, self.width as usize, 4))?; + for y in 0..self.height { + let index = (y * self.row_pitch) as usize; - Ok(array) + unsafe { + ptr::copy_nonoverlapping( + self.raw_buffer.as_ptr().add(index), + self.buffer.as_mut_ptr(), + self.width as usize, + ); + } } + + Ok(self.buffer) } /// Save The Frame Buffer As An Image To The Specified Path @@ -339,36 +340,35 @@ impl<'a> FrameBuffer<'a> { &self, path: T, ) -> Result<(), Box> { - let nopadding_buffer = self.as_raw_nopadding_buffer()?; - - let (height, width, _) = nopadding_buffer.dim(); - let mut rgba_image: RgbaImage = RgbaImage::new(width as u32, height as u32); + let mut rgb_image: RgbImage = RgbImage::new(self.width, self.height); if self.color_format == ColorFormat::Rgba8 { - for y in 0..height { - for x in 0..width { - let r = nopadding_buffer[(y, x, 0)]; - let g = nopadding_buffer[(y, x, 1)]; - let b = nopadding_buffer[(y, x, 2)]; - let a = nopadding_buffer[(y, x, 3)]; - - rgba_image.put_pixel(x as u32, y as u32, Rgba([r, g, b, a])); + for y in 0..self.height { + for x in 0..self.width { + let first_index = (y * self.row_pitch + x * 4) as usize; + + let r = self.raw_buffer[first_index]; + let g = self.raw_buffer[first_index + 1]; + let b = self.raw_buffer[first_index + 2]; + + rgb_image.put_pixel(x, y, Rgb([r, g, b])); } } } else { - for y in 0..height { - for x in 0..width { - let b = nopadding_buffer[(y, x, 0)]; - let g = nopadding_buffer[(y, x, 1)]; - let r = nopadding_buffer[(y, x, 2)]; - let a = nopadding_buffer[(y, x, 3)]; - - rgba_image.put_pixel(x as u32, y as u32, Rgba([r, g, b, a])); + for y in 0..self.height { + for x in 0..self.width { + let first_index = (y * self.row_pitch + x * 4) as usize; + + let b = self.raw_buffer[first_index]; + let g = self.raw_buffer[first_index + 1]; + let r = self.raw_buffer[first_index + 2]; + + rgb_image.put_pixel(x, y, Rgb([r, g, b])); } } } - rgba_image.save(path)?; + rgb_image.save(path)?; Ok(()) } diff --git a/src/graphics_capture_api.rs b/src/graphics_capture_api.rs index f49d2a8..2925c6c 100644 --- a/src/graphics_capture_api.rs +++ b/src/graphics_capture_api.rs @@ -119,6 +119,10 @@ impl GraphicsCaptureApi { trace!("Creating Capture Session"); let session = frame_pool.CreateCaptureSession(&item)?; + // Preallocate Memory + trace!("Preallocating Memory"); + let mut buffer = vec![0u8; 3840 * 2160 * 4]; + // Trigger Struct let callback = Arc::new(Mutex::new(callback)); @@ -217,10 +221,11 @@ impl GraphicsCaptureApi { let texture_height = desc.Height; // Create A Frame - let frame = Frame::new( + let mut frame = Frame::new( &d3d_device_frame_pool, frame_surface, &context, + &mut buffer, texture_width, texture_height, color_format, @@ -233,7 +238,7 @@ impl GraphicsCaptureApi { // Send The Frame To Trigger Struct let result = callback_frame_pool .lock() - .on_frame_arrived(frame, internal_capture_control); + .on_frame_arrived(&mut frame, internal_capture_control); if stop.load(atomic::Ordering::Relaxed) || result.is_err() { let _ = RESULT diff --git a/src/lib.rs b/src/lib.rs index 56d1ec6..1b63919 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,7 +22,7 @@ //! //! ```toml //! [dependencies] -//! windows-capture = "1.0.29" +//! windows-capture = "1.0.32" //! ``` //! or run this command //! @@ -60,7 +60,7 @@ //! // Called Every Time A New Frame Is Available //! fn on_frame_arrived( //! &mut self, -//! mut frame: Frame, +//! frame: &mut Frame, //! capture_control: InternalCaptureControl, //! ) -> Result<(), Box> { //! println!("New Frame Arrived"); diff --git a/windows-capture-python/Cargo.lock b/windows-capture-python/Cargo.lock index dd7b9a5..7ef221b 100644 --- a/windows-capture-python/Cargo.lock +++ b/windows-capture-python/Cargo.lock @@ -131,7 +131,7 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.29" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" dependencies = [ diff --git a/windows-capture-python/Cargo.toml b/windows-capture-python/Cargo.toml index 332621e..6619cc8 100644 --- a/windows-capture-python/Cargo.toml +++ b/windows-capture-python/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "windows-capture-python" -version = "1.0.29" +version = "1.0.32" authors = ["NiiightmareXD"] edition = "2021" description = "Fastest Windows Screen Capture Library For Python 🔥" diff --git a/windows-capture-python/pyproject.toml b/windows-capture-python/pyproject.toml index 8c6d56e..f0ea391 100644 --- a/windows-capture-python/pyproject.toml +++ b/windows-capture-python/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "maturin" [project] name = "windows-capture" -version = "1.0.29" +version = "1.0.32" description = "Fastest Windows Screen Capture Library For Python 🔥" readme = "README.md" requires-python = ">=3.9" diff --git a/windows-capture-python/src/lib.rs b/windows-capture-python/src/lib.rs index 238126c..c09176c 100644 --- a/windows-capture-python/src/lib.rs +++ b/windows-capture-python/src/lib.rs @@ -298,14 +298,13 @@ impl WindowsCaptureHandler for InnerNativeWindowsCapture { fn on_frame_arrived( &mut self, - mut frame: Frame, + frame: &mut Frame, capture_control: InternalCaptureControl, ) -> Result<(), Box<(dyn Error + Send + Sync)>> { let width = frame.width(); let height = frame.height(); - let buf = frame.buffer()?; - - let buf = buf.as_raw_buffer(); + let buffer = frame.buffer()?; + let buffer = buffer.as_raw_buffer(); Python::with_gil(|py| -> PyResult<()> { py.check_signals()?; @@ -313,7 +312,13 @@ impl WindowsCaptureHandler for InnerNativeWindowsCapture { let stop_list = PyList::new(py, [false]); self.on_frame_arrived_callback.call1( py, - (buf.as_ptr() as isize, buf.len(), width, height, stop_list), + ( + buffer.as_ptr() as isize, + buffer.len(), + width, + height, + stop_list, + ), )?; if stop_list[0].is_true()? {