From 6eed45f13a3f0ccf578621a1918e58745127ed9e Mon Sep 17 00:00:00 2001 From: NiiightmareXD Date: Tue, 9 Jan 2024 03:56:40 -0800 Subject: [PATCH] =?UTF-8?q?Native=20Windows=20Encoder=20=F0=9F=94=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 246 ------------------------------------ Cargo.toml | 5 +- README.md | 14 +- examples/basic.rs | 14 +- src/capture.rs | 5 +- src/d3d11.rs | 10 +- src/frame.rs | 126 ++++++++++-------- src/graphics_capture_api.rs | 28 ++-- src/lib.rs | 14 +- src/settings.rs | 6 +- 10 files changed, 124 insertions(+), 344 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index aa35465..3cc062f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,63 +2,24 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - [[package]] name = "autocfg" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" -[[package]] -name = "bit_field" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" - [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" -[[package]] -name = "bytemuck" -version = "1.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6" - -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "color_quant" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" - -[[package]] -name = "crc32fast" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" -dependencies = [ - "cfg-if", -] - [[package]] name = "crossbeam-deque" version = "0.8.4" @@ -90,127 +51,24 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - [[package]] name = "either" version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" -[[package]] -name = "exr" -version = "1.71.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "832a761f35ab3e6664babfbdc6cef35a4860e816ec3916dcfd0882954e98a8a8" -dependencies = [ - "bit_field", - "flume", - "half", - "lebe", - "miniz_oxide", - "rayon-core", - "smallvec", - "zune-inflate", -] - -[[package]] -name = "fdeflate" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "209098dd6dfc4445aa6111f0e98653ac323eaa4dfd212c9ca3931bf9955c31bd" -dependencies = [ - "simd-adler32", -] - -[[package]] -name = "flate2" -version = "1.0.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" -dependencies = [ - "crc32fast", - "miniz_oxide", -] - -[[package]] -name = "flume" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" -dependencies = [ - "spin", -] - -[[package]] -name = "gif" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80792593675e051cf94a4b111980da2ba60d4a83e43e0048c5693baab3977045" -dependencies = [ - "color_quant", - "weezl", -] - -[[package]] -name = "half" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b4af3693f1b705df946e9fe5631932443781d0aabb423b62fcd4d73f6d2fd0" -dependencies = [ - "crunchy", -] - [[package]] name = "heck" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" -[[package]] -name = "image" -version = "0.24.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f3dfdbdd72063086ff443e297b61695500514b1e41095b6fb9a5ab48a70a711" -dependencies = [ - "bytemuck", - "byteorder", - "color_quant", - "exr", - "gif", - "jpeg-decoder", - "num-rational", - "num-traits", - "png", - "qoi", - "tiff", -] - [[package]] name = "indoc" version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e186cfbae8084e513daff4240b4797e342f988cecda4fb6c939150f96315fd8" -[[package]] -name = "jpeg-decoder" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc0000e42512c92e31c2252315bda326620a4e034105e900c98ec492fa077b3e" -dependencies = [ - "rayon", -] - -[[package]] -name = "lebe" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" - [[package]] name = "libc" version = "0.2.151" @@ -242,46 +100,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "miniz_oxide" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" -dependencies = [ - "adler", - "simd-adler32", -] - -[[package]] -name = "num-integer" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" -dependencies = [ - "autocfg", - "num-traits", -] - -[[package]] -name = "num-rational" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" -dependencies = [ - "autocfg", -] - [[package]] name = "once_cell" version = "1.19.0" @@ -311,19 +129,6 @@ dependencies = [ "windows-targets 0.48.5", ] -[[package]] -name = "png" -version = "0.17.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd75bf2d8dd3702b9707cdbc56a5b9ef42cec752eb8b3bafc01234558442aa64" -dependencies = [ - "bitflags", - "crc32fast", - "fdeflate", - "flate2", - "miniz_oxide", -] - [[package]] name = "proc-macro2" version = "1.0.75" @@ -394,15 +199,6 @@ dependencies = [ "syn", ] -[[package]] -name = "qoi" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" -dependencies = [ - "bytemuck", -] - [[package]] name = "quote" version = "1.0.35" @@ -447,27 +243,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" -[[package]] -name = "simd-adler32" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" - [[package]] name = "smallvec" version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" -dependencies = [ - "lock_api", -] - [[package]] name = "syn" version = "2.0.48" @@ -505,17 +286,6 @@ dependencies = [ "syn", ] -[[package]] -name = "tiff" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d172b0f4d3fba17ba89811858b9d3d97f928aece846475bbda076ca46736211" -dependencies = [ - "flate2", - "jpeg-decoder", - "weezl", -] - [[package]] name = "unicode-ident" version = "1.0.12" @@ -528,12 +298,6 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7de7d73e1754487cb58364ee906a499937a0dfabd86bcb980fa99ec8c8fa2ce" -[[package]] -name = "weezl" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb" - [[package]] name = "windows" version = "0.52.0" @@ -548,7 +312,6 @@ dependencies = [ name = "windows-capture" version = "1.0.56" dependencies = [ - "image", "log", "parking_lot", "rayon", @@ -687,12 +450,3 @@ name = "windows_x86_64_msvc" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" - -[[package]] -name = "zune-inflate" -version = "0.2.54" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" -dependencies = [ - "simd-adler32", -] diff --git a/Cargo.toml b/Cargo.toml index c9b9251..882f943 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,6 @@ categories = [ resolver = "2" [dependencies] -image = "0.24.7" log = "0.4.20" parking_lot = "0.12.1" rayon = "1.8.0" @@ -39,6 +38,10 @@ windows = { version = "0.52.0", features = [ "Graphics_DirectX_Direct3D11", "Foundation_Metadata", "Win32_Devices_Display", + "Storage", + "Graphics_Imaging", + "Storage_Streams", + "Foundation", ] } [package.metadata.docs.rs] diff --git a/README.md b/README.md index 2eda778..17b7209 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ cargo add windows-capture ```rust use windows_capture::{ capture::WindowsCaptureHandler, - frame::Frame, + frame::{Frame, ImageFormat}, graphics_capture_api::InternalCaptureControl, settings::{ColorFormat, Settings}, window::Window, @@ -49,10 +49,10 @@ use windows_capture::{ struct Capture; impl WindowsCaptureHandler for Capture { - // To Get The Message From The Settings + // Any Value To Get From The Settings type Flags = String; - // To Redirect To CaptureControl Or Start Method + // To Redirect To `CaptureControl` Or Start Method type Error = Box; // Function That Will Be Called To Create The Struct The Flags Can Be Passed @@ -72,7 +72,7 @@ impl WindowsCaptureHandler for Capture { println!("New Frame Arrived"); // Save The Frame As An Image To The Specified Path - frame.save_as_image("image.png")?; + frame.save_as_image("image.png", ImageFormat::Png)?; // Gracefully Stop The Capture Thread capture_control.stop(); @@ -90,7 +90,7 @@ impl WindowsCaptureHandler for Capture { } fn main() { - // Checkout Docs For Other Capture Items + // Gets The Foreground Window, Checkout The Docs For Other Capture Items let foreground_window = Window::foreground().expect("No Active Window Found"); let settings = Settings::new( @@ -102,13 +102,13 @@ fn main() { None, // Kind Of Pixel Format For Frame To Have ColorFormat::Rgba8, - // Will Be Passed To The New Function + // Any Value To Pass To The New Function "It Works".to_string(), ) .unwrap(); // Every Error From `new`, `on_frame_arrived` and `on_closed` Will End Up Here - Capture::start(settings).unwrap(); + Capture::start(settings).expect("Screen Capture Failed"); } ``` diff --git a/examples/basic.rs b/examples/basic.rs index ad56afd..d60292f 100644 --- a/examples/basic.rs +++ b/examples/basic.rs @@ -1,6 +1,6 @@ use windows_capture::{ capture::WindowsCaptureHandler, - frame::Frame, + frame::{Frame, ImageFormat}, graphics_capture_api::InternalCaptureControl, settings::{ColorFormat, Settings}, window::Window, @@ -10,10 +10,10 @@ use windows_capture::{ struct Capture; impl WindowsCaptureHandler for Capture { - // To Get The Message From The Settings + // Any Value To Get From The Settings type Flags = String; - // To Redirect To CaptureControl Or Start Method + // To Redirect To `CaptureControl` Or Start Method type Error = Box; // Function That Will Be Called To Create The Struct The Flags Can Be Passed @@ -33,7 +33,7 @@ impl WindowsCaptureHandler for Capture { println!("New Frame Arrived"); // Save The Frame As An Image To The Specified Path - frame.save_as_image("image.png")?; + frame.save_as_image("image.png", ImageFormat::Png)?; // Gracefully Stop The Capture Thread capture_control.stop(); @@ -51,7 +51,7 @@ impl WindowsCaptureHandler for Capture { } fn main() { - // Checkout Docs For Other Capture Items + // Gets The Foreground Window, Checkout The Docs For Other Capture Items let foreground_window = Window::foreground().expect("No Active Window Found"); let settings = Settings::new( @@ -63,11 +63,11 @@ fn main() { None, // Kind Of Pixel Format For Frame To Have ColorFormat::Rgba8, - // Will Be Passed To The New Function + // Any Value To Pass To The New Function "It Works".to_string(), ) .unwrap(); // Every Error From `new`, `on_frame_arrived` and `on_closed` Will End Up Here - Capture::start(settings).unwrap(); + Capture::start(settings).expect("Screen Capture Failed"); } diff --git a/src/capture.rs b/src/capture.rs index db8b4bc..6ba3876 100644 --- a/src/capture.rs +++ b/src/capture.rs @@ -18,7 +18,8 @@ use windows::{ Threading::{GetCurrentThreadId, GetThreadId}, WinRT::{ CreateDispatcherQueueController, DispatcherQueueOptions, RoInitialize, - RoUninitialize, DQTAT_COM_NONE, DQTYPE_THREAD_CURRENT, RO_INIT_SINGLETHREADED, + RoUninitialize, DQTAT_COM_NONE, DQTYPE_THREAD_CURRENT, RO_INIT_MULTITHREADED, + RO_INIT_SINGLETHREADED, }, }, UI::WindowsAndMessaging::{ @@ -193,7 +194,7 @@ pub trait WindowsCaptureHandler: Sized { // Initialize WinRT trace!("Initializing WinRT"); unsafe { - RoInitialize(RO_INIT_SINGLETHREADED) + RoInitialize(RO_INIT_MULTITHREADED) .map_err(|_| WindowsCaptureError::FailedToInitWinRT)?; }; diff --git a/src/d3d11.rs b/src/d3d11.rs index 8fc2980..bda87fe 100644 --- a/src/d3d11.rs +++ b/src/d3d11.rs @@ -10,7 +10,7 @@ use windows::{ }, Direct3D11::{ D3D11CreateDevice, ID3D11Device, ID3D11DeviceContext, - D3D11_CREATE_DEVICE_BGRA_SUPPORT, D3D11_CREATE_DEVICE_FLAG, D3D11_SDK_VERSION, + D3D11_CREATE_DEVICE_BGRA_SUPPORT, D3D11_SDK_VERSION, }, Dxgi::IDXGIDevice, }, @@ -40,7 +40,7 @@ pub enum Error { } /// Create `ID3D11Device` An`ID3D11DeviceContext`xt -pub fn create_d3d_device(bgra_support: bool) -> Result<(ID3D11Device, ID3D11DeviceContext), Error> { +pub fn create_d3d_device() -> Result<(ID3D11Device, ID3D11DeviceContext), Error> { // Set Feature Flags let feature_flags = [ D3D_FEATURE_LEVEL_11_1, @@ -61,11 +61,7 @@ pub fn create_d3d_device(bgra_support: bool) -> Result<(ID3D11Device, ID3D11Devi None, D3D_DRIVER_TYPE_HARDWARE, None, - if bgra_support { - D3D11_CREATE_DEVICE_BGRA_SUPPORT - } else { - D3D11_CREATE_DEVICE_FLAG(0) - }, + D3D11_CREATE_DEVICE_BGRA_SUPPORT, Some(&feature_flags), D3D11_SDK_VERSION, Some(&mut d3d_device), diff --git a/src/frame.rs b/src/frame.rs index 55e0586..4588040 100644 --- a/src/frame.rs +++ b/src/frame.rs @@ -1,16 +1,22 @@ -use std::{path::Path, ptr, slice}; +use std::{ + fs::{self}, + io, + path::Path, + ptr, slice, +}; -use image::{Rgb, RgbImage}; use log::trace; use rayon::iter::{IntoParallelIterator, ParallelIterator}; -use windows::Win32::Graphics::{ - Direct3D11::{ - ID3D11Device, ID3D11DeviceContext, ID3D11Texture2D, D3D11_BOX, D3D11_CPU_ACCESS_READ, - D3D11_CPU_ACCESS_WRITE, D3D11_MAPPED_SUBRESOURCE, D3D11_MAP_READ_WRITE, - D3D11_TEXTURE2D_DESC, D3D11_USAGE_STAGING, - }, - Dxgi::Common::{ - DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_NV12, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_SAMPLE_DESC, +use windows::{ + Graphics::Imaging::{BitmapAlphaMode, BitmapEncoder, BitmapPixelFormat}, + Storage::Streams::{Buffer, DataReader, InMemoryRandomAccessStream, InputStreamOptions}, + Win32::Graphics::{ + Direct3D11::{ + ID3D11Device, ID3D11DeviceContext, ID3D11Texture2D, D3D11_BOX, D3D11_CPU_ACCESS_READ, + D3D11_CPU_ACCESS_WRITE, D3D11_MAPPED_SUBRESOURCE, D3D11_MAP_READ_WRITE, + D3D11_TEXTURE2D_DESC, D3D11_USAGE_STAGING, + }, + Dxgi::Common::{DXGI_FORMAT, DXGI_SAMPLE_DESC}, }, }; @@ -21,10 +27,24 @@ use crate::settings::ColorFormat; pub enum Error { #[error("Invalid Box Size")] InvalidSize, - #[error(transparent)] - ImageSaveFailed(#[from] image::error::ImageError), + #[error("Invalid Path")] + InvalidPath, + #[error("This Color Format Is Not Supported For Saving As Image")] + UnsupportedFormat, #[error(transparent)] WindowsError(#[from] windows::core::Error), + #[error(transparent)] + IoError(#[from] io::Error), +} + +#[derive(Eq, PartialEq, Clone, Copy, Debug)] +pub enum ImageFormat { + Jpeg, + Png, + Gif, + Tiff, + Bmp, + JpegXr, } /// Frame Struct Used To Get The Frame Buffer @@ -81,11 +101,7 @@ impl<'a> Frame<'a> { Height: self.height, MipLevels: 1, ArraySize: 1, - Format: match self.color_format { - ColorFormat::Rgba8 => DXGI_FORMAT_R8G8B8A8_UNORM, - ColorFormat::Bgra8 => DXGI_FORMAT_B8G8R8A8_UNORM, - ColorFormat::NV12 => DXGI_FORMAT_NV12, - }, + Format: DXGI_FORMAT(self.color_format as i32), SampleDesc: DXGI_SAMPLE_DESC { Count: 1, Quality: 0, @@ -164,11 +180,7 @@ impl<'a> Frame<'a> { Height: texture_height, MipLevels: 1, ArraySize: 1, - Format: if self.color_format == ColorFormat::Rgba8 { - DXGI_FORMAT_R8G8B8A8_UNORM - } else { - DXGI_FORMAT_B8G8R8A8_UNORM - }, + Format: DXGI_FORMAT(self.color_format as i32), SampleDesc: DXGI_SAMPLE_DESC { Count: 1, Quality: 0, @@ -246,10 +258,14 @@ impl<'a> Frame<'a> { } /// Save The Frame Buffer As An Image To The Specified Path - pub fn save_as_image>(&mut self, path: T) -> Result<(), Error> { + pub fn save_as_image>( + &mut self, + path: T, + format: ImageFormat, + ) -> Result<(), Error> { let frame_buffer = self.buffer()?; - frame_buffer.save_as_image(path)?; + frame_buffer.save_as_image(path, format)?; Ok(()) } @@ -358,38 +374,48 @@ impl<'a> FrameBuffer<'a> { } /// Save The Frame Buffer As An Image To The Specified Path (Only `ColorFormat::Rgba8` And `ColorFormat::Bgra8`) - pub fn save_as_image>(&self, path: T) -> Result<(), Error> { - let mut rgb_image: RgbImage = RgbImage::new(self.width, self.height); + pub fn save_as_image>(&self, path: T, format: ImageFormat) -> Result<(), Error> { + let encoder = match format { + ImageFormat::Jpeg => BitmapEncoder::JpegEncoderId()?, + ImageFormat::Png => BitmapEncoder::PngEncoderId()?, + ImageFormat::Gif => BitmapEncoder::GifEncoderId()?, + ImageFormat::Tiff => BitmapEncoder::TiffEncoderId()?, + ImageFormat::Bmp => BitmapEncoder::BmpEncoderId()?, + ImageFormat::JpegXr => BitmapEncoder::JpegXREncoderId()?, + }; - if self.color_format == ColorFormat::Rgba8 { - for y in 0..self.height { - for x in 0..self.width { - let first_index = (y * self.row_pitch + x * 4) as usize; + let stream = InMemoryRandomAccessStream::new()?; + let encoder = BitmapEncoder::CreateAsync(encoder, &stream)?.get()?; - let r = self.raw_buffer[first_index]; - let g = self.raw_buffer[first_index + 1]; - let b = self.raw_buffer[first_index + 2]; + let pixelformat = match self.color_format { + ColorFormat::Bgra8 => BitmapPixelFormat::Bgra8, + ColorFormat::Rgba8 => BitmapPixelFormat::Rgba8, + ColorFormat::Rgba16F => return Err(Error::UnsupportedFormat), + }; - rgb_image.put_pixel(x, y, Rgb([r, g, b])); - } - } - } else if self.color_format == ColorFormat::Bgra8 { - for y in 0..self.height { - for x in 0..self.width { - let first_index = (y * self.row_pitch + x * 4) as usize; + encoder.SetPixelData( + pixelformat, + BitmapAlphaMode::Premultiplied, + self.width, + self.height, + 1.0, + 1.0, + self.raw_buffer, + )?; - let b = self.raw_buffer[first_index]; - let g = self.raw_buffer[first_index + 1]; - let r = self.raw_buffer[first_index + 2]; + encoder.FlushAsync()?.get()?; - rgb_image.put_pixel(x, y, Rgb([r, g, b])); - } - } - } else { - unimplemented!() - } + let buffer = Buffer::Create(u32::try_from(stream.Size()?).unwrap())?; + stream + .ReadAsync(&buffer, buffer.Capacity()?, InputStreamOptions::None)? + .get()?; + + let data_reader = DataReader::FromBuffer(&buffer)?; + let length = data_reader.UnconsumedBufferLength()?; + let mut bytes = vec![0u8; length as usize]; + data_reader.ReadBytes(&mut bytes).unwrap(); - rgb_image.save(path)?; + fs::write(path, bytes)?; Ok(()) } diff --git a/src/graphics_capture_api.rs b/src/graphics_capture_api.rs index ff97db3..456e904 100644 --- a/src/graphics_capture_api.rs +++ b/src/graphics_capture_api.rs @@ -91,24 +91,24 @@ impl GraphicsCaptureApi { result: Arc>>, ) -> Result { // Check Support - if !ApiInformation::IsApiContractPresentByMajor( - &HSTRING::from("Windows.Foundation.UniversalApiContract"), - 8, - )? { + if !Self::is_supported()? { return Err(Error::Unsupported); } + if draw_border.is_some() && !Self::is_border_toggle_supported()? { + return Err(Error::BorderConfigUnsupported); + } + + if capture_cursor.is_some() && !Self::is_cursor_toggle_supported()? { + return Err(Error::CursorConfigUnsupported); + } + // Create DirectX Devices trace!("Creating DirectX Devices"); - let (d3d_device, d3d_device_context) = - create_d3d_device(color_format == ColorFormat::Bgra8)?; + let (d3d_device, d3d_device_context) = create_d3d_device()?; let direct3d_device = create_direct3d_device(&d3d_device)?; - let pixel_format = match color_format { - ColorFormat::Rgba8 => DirectXPixelFormat::R8G8B8A8UIntNormalized, - ColorFormat::Bgra8 => DirectXPixelFormat::B8G8R8A8UIntNormalized, - ColorFormat::NV12 => DirectXPixelFormat::NV12, - }; + let pixel_format = DirectXPixelFormat(color_format as i32); // Create Frame Pool trace!("Creating Frame Pool"); @@ -348,7 +348,7 @@ impl GraphicsCaptureApi { Ok(ApiInformation::IsApiContractPresentByMajor( &HSTRING::from("Windows.Foundation.UniversalApiContract"), 8, - )?) + )? && GraphicsCaptureSession::IsSupported()?) } /// Check If You Can Toggle The Cursor On Or Off @@ -356,7 +356,7 @@ impl GraphicsCaptureApi { Ok(ApiInformation::IsPropertyPresent( &HSTRING::from("Windows.Graphics.Capture.GraphicsCaptureSession"), &HSTRING::from("IsCursorCaptureEnabled"), - )?) + )? && Self::is_supported()?) } /// Check If You Can Toggle The Border On Or Off @@ -364,7 +364,7 @@ impl GraphicsCaptureApi { Ok(ApiInformation::IsPropertyPresent( &HSTRING::from("Windows.Graphics.Capture.GraphicsCaptureSession"), &HSTRING::from("IsBorderRequired"), - )?) + )? && Self::is_supported()?) } } diff --git a/src/lib.rs b/src/lib.rs index 0c20aa1..be48baf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -35,7 +35,7 @@ //! ```no_run //! use windows_capture::{ //! capture::WindowsCaptureHandler, -//! frame::Frame, +//! frame::{Frame, ImageFormat}, //! graphics_capture_api::InternalCaptureControl, //! settings::{ColorFormat, Settings}, //! window::Window, @@ -45,10 +45,10 @@ //! struct Capture; //! //! impl WindowsCaptureHandler for Capture { -//! // To Get The Message From The Settings +//! // Any Value To Get From The Settings //! type Flags = String; //! -//! // To Redirect To CaptureControl Or Start Method +//! // To Redirect To `CaptureControl` Or Start Method //! type Error = Box; //! //! // Function That Will Be Called To Create The Struct The Flags Can Be Passed @@ -68,7 +68,7 @@ //! println!("New Frame Arrived"); //! //! // Save The Frame As An Image To The Specified Path -//! frame.save_as_image("image.png")?; +//! frame.save_as_image("image.png", ImageFormat::Png)?; //! //! // Gracefully Stop The Capture Thread //! capture_control.stop(); @@ -85,7 +85,7 @@ //! } //! } //! -//! // Checkout Docs For Other Capture Items +//! // Gets The Foreground Window, Checkout The Docs For Other Capture Items //! let foreground_window = Window::foreground().expect("No Active Window Found"); //! //! let settings = Settings::new( @@ -97,13 +97,13 @@ //! None, //! // Kind Of Pixel Format For Frame To Have //! ColorFormat::Rgba8, -//! // Will Be Passed To The New Function +//! // Any Value To Pass To The New Function //! "It Works".to_string(), //! ) //! .unwrap(); //! //! // Every Error From `new`, `on_frame_arrived` and `on_closed` Will End Up Here -//! Capture::start(settings).unwrap(); +//! Capture::start(settings).expect("Screen Capture Failed"); //! ``` #![warn(clippy::pedantic)] #![warn(clippy::nursery)] diff --git a/src/settings.rs b/src/settings.rs index 06e1dd6..dd3c6e1 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -10,9 +10,9 @@ pub enum Error { /// Kind Of Pixel Format For Frame To Have #[derive(Eq, PartialEq, Clone, Copy, Debug)] pub enum ColorFormat { - Rgba8, - Bgra8, - NV12, + Rgba16F = 10, + Rgba8 = 28, + Bgra8 = 87, } /// Capture Settings, None Means Default