mirror of
https://github.com/DarthAffe/ScreenCapture.NET.git
synced 2025-12-12 13:28:35 +00:00
WIP (not working!) First draft for generic color format handling
This commit is contained in:
parent
af401448e7
commit
2cc8a9b6ac
@ -185,11 +185,11 @@ public sealed class DX11ScreenCapture : IScreenCapture
|
||||
|
||||
MappedSubresource mapSource = _context.Map(stagingTexture, 0, MapMode.Read, MapFlags.None);
|
||||
IntPtr sourcePtr = mapSource.DataPointer;
|
||||
lock (captureZone.Buffer)
|
||||
lock (captureZone.PixelBuffer)
|
||||
{
|
||||
for (int y = 0; y < captureZone.Height; y++)
|
||||
{
|
||||
Marshal.Copy(sourcePtr, captureZone.Buffer, y * captureZone.Stride, captureZone.Stride);
|
||||
Marshal.Copy(sourcePtr, captureZone.PixelBuffer.Raw, y * captureZone.Stride, captureZone.Stride);
|
||||
sourcePtr += mapSource.RowPitch;
|
||||
}
|
||||
}
|
||||
@ -209,8 +209,7 @@ public sealed class DX11ScreenCapture : IScreenCapture
|
||||
int unscaledHeight = height;
|
||||
(width, height) = CalculateScaledSize(unscaledWidth, unscaledHeight, downscaleLevel);
|
||||
|
||||
byte[] buffer = new byte[width * height * BPP];
|
||||
|
||||
PixelBuffer buffer = new PixelBuffer<ColorBGRA8>(width, height);
|
||||
CaptureZone captureZone = new(_indexCounter++, x, y, width, height, BPP, downscaleLevel, unscaledWidth, unscaledHeight, buffer);
|
||||
lock (_captureZones)
|
||||
InitializeCaptureZone(captureZone);
|
||||
@ -268,7 +267,7 @@ public sealed class DX11ScreenCapture : IScreenCapture
|
||||
captureZone.Width = newWidth;
|
||||
captureZone.Height = newHeight;
|
||||
captureZone.DownscaleLevel = newDownscaleLevel;
|
||||
captureZone.Buffer = new byte[newWidth * newHeight * BPP];
|
||||
captureZone.PixelBuffer = new PixelBuffer<ColorBGRA8>(newWidth, newHeight);
|
||||
|
||||
InitializeCaptureZone(captureZone);
|
||||
}
|
||||
@ -383,7 +382,7 @@ public sealed class DX11ScreenCapture : IScreenCapture
|
||||
}
|
||||
|
||||
if (_useNewDuplicationAdapter)
|
||||
_duplicatedOutput = output.DuplicateOutput1(_device, Format.B8G8R8A8_UNorm); // DarthAffe 27.02.2021: This prepares for the use of 10bit color depth
|
||||
_duplicatedOutput = output.DuplicateOutput1(_device, new[] { Format.B8G8R8A8_UNorm }); // DarthAffe 27.02.2021: This prepares for the use of 10bit color depth
|
||||
else
|
||||
_duplicatedOutput = output.DuplicateOutput(_device);
|
||||
}
|
||||
|
||||
39
ScreenCapture.NET/Helper/MathHelper.cs
Normal file
39
ScreenCapture.NET/Helper/MathHelper.cs
Normal file
@ -0,0 +1,39 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace ScreenCapture.NET;
|
||||
|
||||
public static class MathHelper
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static float Clamp(this float value, float min, float max)
|
||||
{
|
||||
// ReSharper disable ConvertIfStatementToReturnStatement - I'm not sure why, but inlining this statement reduces performance by ~10%
|
||||
if (value < min) return min;
|
||||
if (value > max) return max;
|
||||
return value;
|
||||
// ReSharper restore ConvertIfStatementToReturnStatement
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a normalized float value in the range [0..1] to a byte [0..255].
|
||||
/// </summary>
|
||||
/// <param name="percentage">The normalized float value to convert.</param>
|
||||
/// <returns>The byte value.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static byte GetByteValueFromPercentage(this float percentage)
|
||||
{
|
||||
if (float.IsNaN(percentage)) return 0;
|
||||
|
||||
percentage = percentage.Clamp(0, 1.0f);
|
||||
return (byte)(percentage >= 1.0f ? 255 : percentage * 256.0f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a byte value [0..255] to a normalized float value in the range [0..1].
|
||||
/// </summary>
|
||||
/// <param name="value">The byte value to convert.</param>
|
||||
/// <returns>The normalized float value.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static float GetPercentageFromByteValue(this byte value)
|
||||
=> value == 255 ? 1.0f : (value / 256.0f);
|
||||
}
|
||||
@ -81,7 +81,7 @@ public sealed class BlackBarDetection
|
||||
int stride = _captureZone.Stride;
|
||||
for (int row = 0; row < _captureZone.Height; row++)
|
||||
{
|
||||
Span<byte> data = new(_captureZone.Buffer, row * stride, stride);
|
||||
Span<byte> data = new(_captureZone.PixelBuffer.Raw, row * stride, stride);
|
||||
for (int i = 0; i < data.Length; i += 4)
|
||||
if ((data[i] > threshold) || (data[i + 1] > threshold) || (data[i + 2] > threshold))
|
||||
return row;
|
||||
@ -96,7 +96,7 @@ public sealed class BlackBarDetection
|
||||
int stride = _captureZone.Stride;
|
||||
for (int row = _captureZone.Height - 1; row >= 0; row--)
|
||||
{
|
||||
Span<byte> data = new(_captureZone.Buffer, row * stride, stride);
|
||||
Span<byte> data = new(_captureZone.PixelBuffer.Raw, row * stride, stride);
|
||||
for (int i = 0; i < data.Length; i += 4)
|
||||
if ((data[i] > threshold) || (data[i + 1] > threshold) || (data[i + 2] > threshold))
|
||||
return (_captureZone.Height - 1) - row;
|
||||
@ -109,7 +109,7 @@ public sealed class BlackBarDetection
|
||||
{
|
||||
int threshold = Threshold;
|
||||
int stride = _captureZone.Stride;
|
||||
byte[] buffer = _captureZone.Buffer;
|
||||
byte[] buffer = _captureZone.PixelBuffer.Raw;
|
||||
for (int column = 0; column < _captureZone.Width; column++)
|
||||
for (int row = 0; row < _captureZone.Height; row++)
|
||||
{
|
||||
@ -125,7 +125,7 @@ public sealed class BlackBarDetection
|
||||
{
|
||||
int threshold = Threshold;
|
||||
int stride = _captureZone.Stride;
|
||||
byte[] buffer = _captureZone.Buffer;
|
||||
byte[] buffer = _captureZone.PixelBuffer.Raw;
|
||||
for (int column = _captureZone.Width - 1; column >= 0; column--)
|
||||
for (int row = 0; row < _captureZone.Height; row++)
|
||||
{
|
||||
|
||||
@ -19,37 +19,58 @@ public sealed class CaptureZone
|
||||
/// <summary>
|
||||
/// Gets the x-location of the region on the screen.
|
||||
/// </summary>
|
||||
public int X { get; internal set; }
|
||||
/// <remarks>
|
||||
/// Should only be set inside of ScreenCaptures!
|
||||
/// </remarks>
|
||||
public int X { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the y-location of the region on the screen.
|
||||
/// </summary>
|
||||
public int Y { get; internal set; }
|
||||
/// <remarks>
|
||||
/// Should only be set inside of ScreenCaptures!
|
||||
/// </remarks>
|
||||
public int Y { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the width of the captured region.
|
||||
/// </summary>
|
||||
public int Width { get; internal set; }
|
||||
/// <remarks>
|
||||
/// Should only be set inside of ScreenCaptures!
|
||||
/// </remarks>
|
||||
public int Width { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the height of the captured region.
|
||||
/// </summary>
|
||||
public int Height { get; internal set; }
|
||||
/// <remarks>
|
||||
/// Should only be set inside of ScreenCaptures!
|
||||
/// </remarks>
|
||||
public int Height { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the level of downscaling applied to the image of this region before copying to local memory. The calculation is (width and height)/2^downscaleLevel.
|
||||
/// </summary>
|
||||
public int DownscaleLevel { get; internal set; }
|
||||
/// <remarks>
|
||||
/// Should only be set inside of ScreenCaptures!
|
||||
/// </remarks>
|
||||
public int DownscaleLevel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the original width of the region (this equals <see cref="Width"/> if <see cref="DownscaleLevel"/> is 0).
|
||||
/// </summary>
|
||||
public int UnscaledWidth { get; internal set; }
|
||||
/// <remarks>
|
||||
/// Should only be set inside of ScreenCaptures!
|
||||
/// </remarks>
|
||||
public int UnscaledWidth { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the original height of the region (this equals <see cref="Height"/> if <see cref="DownscaleLevel"/> is 0).
|
||||
/// </summary>
|
||||
public int UnscaledHeight { get; internal set; }
|
||||
/// <remarks>
|
||||
/// Should only be set inside of ScreenCaptures!
|
||||
/// </remarks>
|
||||
public int UnscaledHeight { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the amount of bytes per pixel in the image (most likely 3 [RGB] or 4 [ARGB]).
|
||||
@ -64,7 +85,10 @@ public sealed class CaptureZone
|
||||
/// <summary>
|
||||
/// Gets the buffer containing the image data. Format depends on the specific capture but is most likely BGRA32.
|
||||
/// </summary>
|
||||
public byte[] Buffer { get; internal set; }
|
||||
/// <remarks>
|
||||
/// Should only be set inside of ScreenCaptures!
|
||||
/// </remarks>
|
||||
public PixelBuffer PixelBuffer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the config for black-bar detection.
|
||||
@ -107,7 +131,7 @@ public sealed class CaptureZone
|
||||
/// <param name="unscaledWidth">The original width of the region.</param>
|
||||
/// <param name="unscaledHeight">The original height of the region</param>
|
||||
/// <param name="buffer">The buffer containing the image data.</param>
|
||||
internal CaptureZone(int id, int x, int y, int width, int height, int bytesPerPixel, int downscaleLevel, int unscaledWidth, int unscaledHeight, byte[] buffer)
|
||||
public CaptureZone(int id, int x, int y, int width, int height, int bytesPerPixel, int downscaleLevel, int unscaledWidth, int unscaledHeight, PixelBuffer buffer)
|
||||
{
|
||||
this.Id = id;
|
||||
this.X = x;
|
||||
@ -118,7 +142,7 @@ public sealed class CaptureZone
|
||||
this.UnscaledWidth = unscaledWidth;
|
||||
this.UnscaledHeight = unscaledHeight;
|
||||
this.DownscaleLevel = downscaleLevel;
|
||||
this.Buffer = buffer;
|
||||
this.PixelBuffer = buffer;
|
||||
|
||||
BlackBars = new BlackBarDetection(this);
|
||||
}
|
||||
|
||||
41
ScreenCapture.NET/Model/Colors/ColorBGRA8.cs
Normal file
41
ScreenCapture.NET/Model/Colors/ColorBGRA8.cs
Normal file
@ -0,0 +1,41 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace ScreenCapture.NET;
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public readonly struct ColorBGRA8 : IColor
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
private readonly byte _r;
|
||||
private readonly byte _g;
|
||||
private readonly byte _b;
|
||||
private readonly byte _a;
|
||||
|
||||
public byte A => _a;
|
||||
public byte R => _r;
|
||||
public byte G => _g;
|
||||
public byte B => _b;
|
||||
|
||||
public float sA => _a.GetPercentageFromByteValue();
|
||||
public float sR => _r.GetPercentageFromByteValue();
|
||||
public float sG => _g.GetPercentageFromByteValue();
|
||||
public float sB => _b.GetPercentageFromByteValue();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
public ColorBGRA8()
|
||||
{ }
|
||||
|
||||
public ColorBGRA8(byte r, byte g, byte b, byte a)
|
||||
{
|
||||
this._r = r;
|
||||
this._g = g;
|
||||
this._b = b;
|
||||
this._a = a;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
14
ScreenCapture.NET/Model/Colors/IColor.cs
Normal file
14
ScreenCapture.NET/Model/Colors/IColor.cs
Normal file
@ -0,0 +1,14 @@
|
||||
namespace ScreenCapture.NET;
|
||||
|
||||
public interface IColor
|
||||
{
|
||||
byte A { get; }
|
||||
byte R { get; }
|
||||
byte G { get; }
|
||||
byte B { get; }
|
||||
|
||||
float sA { get; }
|
||||
float sR { get; }
|
||||
float sG { get; }
|
||||
float sB { get; }
|
||||
}
|
||||
56
ScreenCapture.NET/Model/PixelBuffer.cs
Normal file
56
ScreenCapture.NET/Model/PixelBuffer.cs
Normal file
@ -0,0 +1,56 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using CommunityToolkit.HighPerformance;
|
||||
|
||||
namespace ScreenCapture.NET;
|
||||
|
||||
public abstract class PixelBuffer
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
public byte[] Raw { get; }
|
||||
|
||||
public abstract ReadOnlySpan2D<IColor> Pixels { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
protected PixelBuffer(byte[] buffer)
|
||||
{
|
||||
this.Raw = buffer;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public sealed class PixelBuffer<TColor> : PixelBuffer
|
||||
where TColor : unmanaged, IColor
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
private readonly int _width;
|
||||
private readonly int _height;
|
||||
|
||||
public override unsafe ReadOnlySpan2D<IColor> Pixels
|
||||
{
|
||||
get
|
||||
{
|
||||
TColor @ref = MemoryMarshal.AsRef<TColor>(Raw);
|
||||
return new ReadOnlySpan2D<IColor>(Unsafe.AsPointer(ref @ref), _height, _width, 0);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
public PixelBuffer(int width, int height)
|
||||
: base(new byte[width * height * Marshal.SizeOf<TColor>()])
|
||||
{
|
||||
this._width = width;
|
||||
this._height = height;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net6.0;net5.0</TargetFrameworks>
|
||||
<TargetFrameworks>net7.0;net6.0</TargetFrameworks>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
@ -31,9 +31,9 @@
|
||||
- Fixed ambiguous equals in Display
|
||||
</PackageReleaseNotes>
|
||||
|
||||
<Version>1.2.0</Version>
|
||||
<AssemblyVersion>1.2.0</AssemblyVersion>
|
||||
<FileVersion>1.2.0</FileVersion>
|
||||
<Version>2.0.0</Version>
|
||||
<AssemblyVersion>2.0.0</AssemblyVersion>
|
||||
<FileVersion>2.0.0</FileVersion>
|
||||
|
||||
<OutputPath>..\bin\</OutputPath>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
@ -64,6 +64,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CommunityToolkit.HighPerformance" Version="8.1.0" />
|
||||
<PackageReference Include="Vortice.Direct3D11" Version="1.9.143" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@ -2,4 +2,5 @@
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=directx/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=events/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=helper/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=model/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=model/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=model_005Ccolors/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||
Loading…
x
Reference in New Issue
Block a user