From 5b2c83a3e1fe0439be04c2fc0c24ec079cb20ceb Mon Sep 17 00:00:00 2001 From: Darth Affe Date: Sun, 7 May 2023 23:19:46 +0200 Subject: [PATCH 1/3] Added support for rotated screens --- .../DirectX/DX11ScreenCapture.cs | 98 +++++++++++++++++-- .../DirectX/DX11ScreenCaptureService.cs | 10 +- ScreenCapture.NET/Model/Display.cs | 7 +- ScreenCapture.NET/Model/Rotation.cs | 9 ++ 4 files changed, 114 insertions(+), 10 deletions(-) create mode 100644 ScreenCapture.NET/Model/Rotation.cs diff --git a/ScreenCapture.NET/DirectX/DX11ScreenCapture.cs b/ScreenCapture.NET/DirectX/DX11ScreenCapture.cs index 10a3817..9842f59 100644 --- a/ScreenCapture.NET/DirectX/DX11ScreenCapture.cs +++ b/ScreenCapture.NET/DirectX/DX11ScreenCapture.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; using System.Threading; using SharpGen.Runtime; using Vortice.Direct3D; @@ -184,13 +184,26 @@ public sealed class DX11ScreenCapture : IScreenCapture captureZone.Y + captureZone.UnscaledHeight, 1)); MappedSubresource mapSource = _context.Map(stagingTexture, 0, MapMode.Read, MapFlags.None); - IntPtr sourcePtr = mapSource.DataPointer; lock (captureZone.Buffer) { - for (int y = 0; y < captureZone.Height; y++) + Span source = mapSource.AsSpan(captureZone.Stride * captureZone.Height); + switch (Display.Rotation) { - Marshal.Copy(sourcePtr, captureZone.Buffer, y * captureZone.Stride, captureZone.Stride); - sourcePtr += mapSource.RowPitch; + case Rotation.Rotation90: + CopyRotate90(source, captureZone); + break; + + case Rotation.Rotation180: + CopyRotate180(source, captureZone); + break; + + case Rotation.Rotation270: + CopyRotate270(source, captureZone); + break; + + default: + CopyRotate0(source, captureZone); + break; } } @@ -200,11 +213,74 @@ public sealed class DX11ScreenCapture : IScreenCapture } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void CopyRotate0(in Span source, in CaptureZone captureZone) => source.CopyTo(captureZone.Buffer.AsSpan()); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void CopyRotate90(in Span source, in CaptureZone captureZone) + { + int width = captureZone.Width; + int height = captureZone.Height; + Span destination = captureZone.Buffer.AsSpan(); + + for (int y = 0; y < height; y++) + for (int x = 0; x < width; x++) + { + int offset = ((y * width) + x) * BPP; + int destinationOffset = ((x * height) + ((height - 1) - y)) * BPP; + destination[destinationOffset] = source[offset]; + destination[destinationOffset + 1] = source[offset + 1]; + destination[destinationOffset + 2] = source[offset + 2]; + destination[destinationOffset + 3] = source[offset + 3]; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void CopyRotate180(in Span source, in CaptureZone captureZone) + { + int width = captureZone.Width; + int height = captureZone.Height; + Span destination = captureZone.Buffer.AsSpan(); + + for (int y = 0; y < height; y++) + for (int x = 0; x < width; x++) + { + int offset = ((y * width) + x) * BPP; + int destinationOffset = destination.Length - offset; + destination[destinationOffset - 4] = source[offset]; + destination[destinationOffset - 3] = source[offset + 1]; + destination[destinationOffset - 2] = source[offset + 2]; + destination[destinationOffset - 1] = source[offset + 3]; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void CopyRotate270(in Span source, in CaptureZone captureZone) + { + int width = captureZone.Width; + int height = captureZone.Height; + Span destination = captureZone.Buffer.AsSpan(); + + for (int y = 0; y < height; y++) + for (int x = 0; x < width; x++) + { + int offset = ((y * width) + x) * BPP; + int destinationOffset = ((((width - 1) - x) * height) + y) * BPP; + destination[destinationOffset] = source[offset]; + destination[destinationOffset + 1] = source[offset + 1]; + destination[destinationOffset + 2] = source[offset + 2]; + destination[destinationOffset + 3] = source[offset + 3]; + } + } + /// public CaptureZone RegisterCaptureZone(int x, int y, int width, int height, int downscaleLevel = 0) { ValidateCaptureZoneAndThrow(x, y, width, height); + if (Display.Rotation is Rotation.Rotation90 or Rotation.Rotation270) + (x, y, width, height) = (y, x, height, width); + int unscaledWidth = width; int unscaledHeight = height; (width, height) = CalculateScaledSize(unscaledWidth, unscaledHeight, downscaleLevel); @@ -252,6 +328,9 @@ public sealed class DX11ScreenCapture : IScreenCapture ValidateCaptureZoneAndThrow(newX, newY, newUnscaledWidth, newUnscaledHeight); + if (Display.Rotation is Rotation.Rotation90 or Rotation.Rotation270) + (newX, newY, newUnscaledWidth, newUnscaledHeight) = (newY, newX, newUnscaledHeight, newUnscaledWidth); + captureZone.X = newX; captureZone.Y = newY; @@ -361,13 +440,18 @@ public sealed class DX11ScreenCapture : IScreenCapture _output = adapter.GetOutput(Display.Index) ?? throw new ApplicationException("Couldn't get DirectX-Output."); using IDXGIOutput5 output = _output.QueryInterface(); + int width = Display.Width; + int height = Display.Height; + if (Display.Rotation is Rotation.Rotation90 or Rotation.Rotation270) + (width, height) = (height, width); + Texture2DDescription captureTextureDesc = new() { CPUAccessFlags = CpuAccessFlags.None, BindFlags = BindFlags.RenderTarget | BindFlags.ShaderResource, Format = Format.B8G8R8A8_UNorm, - Width = Display.Width, - Height = Display.Height, + Width = width, + Height = height, MiscFlags = ResourceOptionFlags.None, MipLevels = 1, ArraySize = 1, diff --git a/ScreenCapture.NET/DirectX/DX11ScreenCaptureService.cs b/ScreenCapture.NET/DirectX/DX11ScreenCaptureService.cs index da70944..79a023e 100644 --- a/ScreenCapture.NET/DirectX/DX11ScreenCaptureService.cs +++ b/ScreenCapture.NET/DirectX/DX11ScreenCaptureService.cs @@ -53,12 +53,20 @@ public class DX11ScreenCaptureService : IScreenCaptureService { int width = output.Description.DesktopCoordinates.Right - output.Description.DesktopCoordinates.Left; int height = output.Description.DesktopCoordinates.Bottom - output.Description.DesktopCoordinates.Top; - yield return new Display(i, output.Description.DeviceName, width, height, graphicsCard); + yield return new Display(i, output.Description.DeviceName, width, height, GetRotation(output.Description.Rotation), graphicsCard); output.Dispose(); i++; } } + private Rotation GetRotation(ModeRotation rotation) => rotation switch + { + ModeRotation.Rotate90 => Rotation.Rotation90, + ModeRotation.Rotate180 => Rotation.Rotation180, + ModeRotation.Rotate270 => Rotation.Rotation270, + _ => Rotation.None + }; + /// public IScreenCapture GetScreenCapture(Display display) { diff --git a/ScreenCapture.NET/Model/Display.cs b/ScreenCapture.NET/Model/Display.cs index bbf67a0..2dc8865 100644 --- a/ScreenCapture.NET/Model/Display.cs +++ b/ScreenCapture.NET/Model/Display.cs @@ -31,6 +31,8 @@ public readonly struct Display /// public int Height { get; } + public Rotation Rotation { get; } + /// /// Gets the this is connected to. /// @@ -48,12 +50,13 @@ public readonly struct Display /// The with of the . /// The height of the . /// The this is connected to. - public Display(int index, string deviceName, int width, int height, GraphicsCard graphicsCard) + public Display(int index, string deviceName, int width, int height, Rotation rotation, GraphicsCard graphicsCard) { this.Index = index; this.DeviceName = deviceName; this.Width = width; this.Height = height; + this.Rotation = rotation; this.GraphicsCard = graphicsCard; } @@ -73,7 +76,7 @@ public readonly struct Display /// public override int GetHashCode() => HashCode.Combine(Index, GraphicsCard); - + /// /// Determines whether two are equal. /// diff --git a/ScreenCapture.NET/Model/Rotation.cs b/ScreenCapture.NET/Model/Rotation.cs new file mode 100644 index 0000000..451add0 --- /dev/null +++ b/ScreenCapture.NET/Model/Rotation.cs @@ -0,0 +1,9 @@ +namespace ScreenCapture.NET; + +public enum Rotation +{ + None = 0, + Rotation90 = 90, + Rotation180 = 180, + Rotation270 = 270 +} From dbcf924b5b35992fcf075cdc6b8a846886dea5e7 Mon Sep 17 00:00:00 2001 From: Darth Affe Date: Sun, 7 May 2023 23:22:56 +0200 Subject: [PATCH 2/3] Added missing doc-comments --- ScreenCapture.NET/Model/Display.cs | 4 ++++ ScreenCapture.NET/Model/Rotation.cs | 3 +++ 2 files changed, 7 insertions(+) diff --git a/ScreenCapture.NET/Model/Display.cs b/ScreenCapture.NET/Model/Display.cs index 2dc8865..cd97780 100644 --- a/ScreenCapture.NET/Model/Display.cs +++ b/ScreenCapture.NET/Model/Display.cs @@ -31,6 +31,9 @@ public readonly struct Display /// public int Height { get; } + /// + /// Gets the rotation of the . + /// public Rotation Rotation { get; } /// @@ -49,6 +52,7 @@ public readonly struct Display /// The name of the . /// The with of the . /// The height of the . + /// The rotation of the . /// The this is connected to. public Display(int index, string deviceName, int width, int height, Rotation rotation, GraphicsCard graphicsCard) { diff --git a/ScreenCapture.NET/Model/Rotation.cs b/ScreenCapture.NET/Model/Rotation.cs index 451add0..8a0aabd 100644 --- a/ScreenCapture.NET/Model/Rotation.cs +++ b/ScreenCapture.NET/Model/Rotation.cs @@ -1,5 +1,8 @@ namespace ScreenCapture.NET; +/// +/// Represents a display-rotation. +/// public enum Rotation { None = 0, From 466f844f201336c1661f02a1b368f3bb9143642c Mon Sep 17 00:00:00 2001 From: Darth Affe Date: Sun, 7 May 2023 23:23:09 +0200 Subject: [PATCH 3/3] Updated vortice to 2.4.2 --- ScreenCapture.NET/ScreenCapture.NET.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ScreenCapture.NET/ScreenCapture.NET.csproj b/ScreenCapture.NET/ScreenCapture.NET.csproj index 9ad1b33..094f20c 100644 --- a/ScreenCapture.NET/ScreenCapture.NET.csproj +++ b/ScreenCapture.NET/ScreenCapture.NET.csproj @@ -62,7 +62,7 @@ - +