mirror of
https://github.com/DarthAffe/ScreenCapture.NET.git
synced 2025-12-12 13:28:35 +00:00
Preparations for a GDI-based capture
This commit is contained in:
parent
d0388f8c75
commit
f05af5fdba
175
ScreenCapture.NET.GDI/GDIScreenCapture.cs
Normal file
175
ScreenCapture.NET.GDI/GDIScreenCapture.cs
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Drawing.Imaging;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Threading;
|
||||||
|
using HPPH;
|
||||||
|
using HPPH.System.Drawing;
|
||||||
|
|
||||||
|
namespace ScreenCapture.NET;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a ScreenCapture using GDI
|
||||||
|
/// </summary>
|
||||||
|
// ReSharper disable once InconsistentNaming
|
||||||
|
public sealed partial class GDIScreenCapture : AbstractScreenCapture<ColorBGRA>
|
||||||
|
{
|
||||||
|
#region DLL-Import
|
||||||
|
|
||||||
|
private const int SRCCOPY = 0x00CC0020;
|
||||||
|
|
||||||
|
[LibraryImport("gdi32.dll")]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
private static partial bool BitBlt(nint hdcDest, int xDest, int yDest, int w, int h, nint hdcSrc, int xSrc, int ySrc, int rop);
|
||||||
|
|
||||||
|
[LibraryImport("user32.dll")]
|
||||||
|
private static partial nint GetDC(nint hwnd);
|
||||||
|
|
||||||
|
[LibraryImport("user32.dll")]
|
||||||
|
private static partial int ReleaseDC(nint hwnd, nint hdc);
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Properties & Fields
|
||||||
|
|
||||||
|
private readonly Lock _captureLock = new();
|
||||||
|
|
||||||
|
private Bitmap? _bitmap;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Constructors
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="GDIScreenCapture"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="display">The <see cref="Display"/> to duplicate.</param>
|
||||||
|
internal GDIScreenCapture(Display display)
|
||||||
|
: base(display)
|
||||||
|
{
|
||||||
|
Restart();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Methods
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override unsafe bool PerformScreenCapture()
|
||||||
|
{
|
||||||
|
lock (_captureLock)
|
||||||
|
{
|
||||||
|
if (_bitmap == null) return false;
|
||||||
|
|
||||||
|
using Graphics gDest = Graphics.FromImage(_bitmap);
|
||||||
|
nint dc = 0;
|
||||||
|
nint dest = 0;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
//dc = GetDC(nint.Zero);
|
||||||
|
//dest = gDest.GetHdc();
|
||||||
|
Stopwatch sw = Stopwatch.StartNew();
|
||||||
|
gDest.CopyFromScreen(0, 0, 0, 0, new Size(Display.Width, 2));
|
||||||
|
//BitBlt(dest, 0, 0, Display.Width, Display.Height, dc, 0, 0, SRCCOPY);
|
||||||
|
Console.WriteLine(sw.Elapsed.TotalMilliseconds);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
gDest.ReleaseHdc(dest);
|
||||||
|
|
||||||
|
if (dc != nint.Zero)
|
||||||
|
ReleaseDC(nint.Zero, dc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void PerformCaptureZoneUpdate(CaptureZone<ColorBGRA> captureZone, Span<byte> buffer)
|
||||||
|
{
|
||||||
|
if (_bitmap == null) return;
|
||||||
|
|
||||||
|
using IDisposable @lock = captureZone.Lock();
|
||||||
|
|
||||||
|
if (captureZone.DownscaleLevel == 0)
|
||||||
|
CopyZone(captureZone, buffer);
|
||||||
|
else
|
||||||
|
DownscaleZone(captureZone, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private unsafe void CopyZone(CaptureZone<ColorBGRA> captureZone, Span<byte> buffer)
|
||||||
|
{
|
||||||
|
if (_bitmap == null) return;
|
||||||
|
|
||||||
|
BitmapData data = _bitmap.LockBits(new Rectangle(0, 0, _bitmap.Width, _bitmap.Height), ImageLockMode.ReadOnly, _bitmap.PixelFormat);
|
||||||
|
ReadOnlySpan<byte> bitmapBuffer = new(data.Scan0.ToPointer(), data.Stride * data.Height);
|
||||||
|
|
||||||
|
RefImage<ColorBGRA>.Wrap(bitmapBuffer, Display.Width, Display.Height, data.Stride)[captureZone.X, captureZone.Y, captureZone.Width, captureZone.Height]
|
||||||
|
.CopyTo(MemoryMarshal.Cast<byte, ColorBGRA>(buffer));
|
||||||
|
|
||||||
|
_bitmap.UnlockBits(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private unsafe void DownscaleZone(CaptureZone<ColorBGRA> captureZone, Span<byte> buffer)
|
||||||
|
{
|
||||||
|
BitmapData data = _bitmap.LockBits(new Rectangle(0, 0, _bitmap.Width, _bitmap.Height), ImageLockMode.ReadOnly, _bitmap.PixelFormat);
|
||||||
|
ReadOnlySpan<byte> bitmapBuffer = new(data.Scan0.ToPointer(), data.Stride * data.Height);
|
||||||
|
|
||||||
|
RefImage<ColorBGRA> source = RefImage<ColorBGRA>.Wrap(bitmapBuffer, Display.Width, Display.Height, data.Stride)[captureZone.X, captureZone.Y, captureZone.UnscaledWidth, captureZone.UnscaledHeight];
|
||||||
|
Span<ColorBGRA> target = MemoryMarshal.Cast<byte, ColorBGRA>(buffer);
|
||||||
|
|
||||||
|
int blockSize = 1 << captureZone.DownscaleLevel;
|
||||||
|
|
||||||
|
int width = captureZone.Width;
|
||||||
|
int height = captureZone.Height;
|
||||||
|
|
||||||
|
for (int y = 0; y < height; y++)
|
||||||
|
for (int x = 0; x < width; x++)
|
||||||
|
target[(y * width) + x] = source[x * blockSize, y * blockSize, blockSize, blockSize].Average();
|
||||||
|
|
||||||
|
_bitmap.UnlockBits(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void Restart()
|
||||||
|
{
|
||||||
|
base.Restart();
|
||||||
|
|
||||||
|
lock (_captureLock)
|
||||||
|
{
|
||||||
|
_bitmap?.Dispose();
|
||||||
|
_bitmap = new Bitmap(Display.Width, Display.Height, PixelFormat.Format32bppArgb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
base.Dispose(disposing);
|
||||||
|
|
||||||
|
lock (_captureLock)
|
||||||
|
DisposeDX();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DisposeDX()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_bitmap?.Dispose();
|
||||||
|
_bitmap = null;
|
||||||
|
}
|
||||||
|
catch { /**/ }
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
78
ScreenCapture.NET.GDI/GDIScreenCaptureService.cs
Normal file
78
ScreenCapture.NET.GDI/GDIScreenCaptureService.cs
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
namespace ScreenCapture.NET;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a <see cref="IScreenCaptureService"/> using the <see cref="GDIScreenCapture"/>.
|
||||||
|
/// </summary>
|
||||||
|
public class GDIScreenCaptureService : IScreenCaptureService
|
||||||
|
{
|
||||||
|
#region Properties & Fields
|
||||||
|
|
||||||
|
private readonly Dictionary<Display, GDIScreenCapture> _screenCaptures = new();
|
||||||
|
|
||||||
|
private bool _isDisposed;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Constructors
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="GDIScreenCaptureService"/> class.
|
||||||
|
/// </summary>
|
||||||
|
public GDIScreenCaptureService()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
~GDIScreenCaptureService() => Dispose();
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Methods
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IEnumerable<GraphicsCard> GetGraphicsCards()
|
||||||
|
{
|
||||||
|
if (_isDisposed) throw new ObjectDisposedException(GetType().FullName);
|
||||||
|
|
||||||
|
return [new GraphicsCard(0, "Default", 0, 0)];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IEnumerable<Display> GetDisplays(GraphicsCard graphicsCard)
|
||||||
|
{
|
||||||
|
if (_isDisposed) throw new ObjectDisposedException(GetType().FullName);
|
||||||
|
|
||||||
|
Screen screen = Screen.PrimaryScreen!;
|
||||||
|
return [new Display(0, screen.DeviceName, screen.Bounds.Width, screen.Bounds.Height, Rotation.None, graphicsCard)];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
IScreenCapture IScreenCaptureService.GetScreenCapture(Display display) => GetScreenCapture(display);
|
||||||
|
public GDIScreenCapture GetScreenCapture(Display display)
|
||||||
|
{
|
||||||
|
if (_isDisposed) throw new ObjectDisposedException(GetType().FullName);
|
||||||
|
|
||||||
|
if (!_screenCaptures.TryGetValue(display, out GDIScreenCapture? screenCapture))
|
||||||
|
_screenCaptures.Add(display, screenCapture = new GDIScreenCapture(display));
|
||||||
|
return screenCapture;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_isDisposed) return;
|
||||||
|
|
||||||
|
foreach (GDIScreenCapture screenCapture in _screenCaptures.Values)
|
||||||
|
screenCapture.Dispose();
|
||||||
|
_screenCaptures.Clear();
|
||||||
|
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
|
||||||
|
_isDisposed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
BIN
ScreenCapture.NET.GDI/Resources/icon.png
Normal file
BIN
ScreenCapture.NET.GDI/Resources/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 705 B |
77
ScreenCapture.NET.GDI/ScreenCapture.NET.GDI.csproj
Normal file
77
ScreenCapture.NET.GDI/ScreenCapture.NET.GDI.csproj
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFrameworks>net8.0-windows;net9.0-windows</TargetFrameworks>
|
||||||
|
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||||
|
<LangVersion>latest</LangVersion>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
|
<UseWindowsForms>true</UseWindowsForms>
|
||||||
|
|
||||||
|
<Authors>Darth Affe</Authors>
|
||||||
|
<Company>Wyrez</Company>
|
||||||
|
<Language>en-US</Language>
|
||||||
|
<NeutralLanguage>en-US</NeutralLanguage>
|
||||||
|
<Title>ScreenCapture.NET.GDI</Title>
|
||||||
|
<AssemblyName>ScreenCapture.NET.GDI</AssemblyName>
|
||||||
|
<AssemblyTitle>ScreenCapture.NET.GDI</AssemblyTitle>
|
||||||
|
<PackageId>ScreenCapture.NET.GDI</PackageId>
|
||||||
|
<RootNamespace>ScreenCapture.NET</RootNamespace>
|
||||||
|
<Description>GDI based Screen-Capturing</Description>
|
||||||
|
<Summary>GDI based Screen-Capturing</Summary>
|
||||||
|
<Copyright>Copyright © Darth Affe 2025</Copyright>
|
||||||
|
<PackageCopyright>Copyright © Darth Affe 2025</PackageCopyright>
|
||||||
|
<PackageIcon>icon.png</PackageIcon>
|
||||||
|
<PackageProjectUrl>https://github.com/DarthAffe/ScreenCapture.NET</PackageProjectUrl>
|
||||||
|
<PackageLicenseExpression>LGPL-2.1-only</PackageLicenseExpression>
|
||||||
|
<RepositoryType>Github</RepositoryType>
|
||||||
|
<RepositoryUrl>https://github.com/DarthAffe/ScreenCapture.NET</RepositoryUrl>
|
||||||
|
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
||||||
|
|
||||||
|
<PackageReleaseNotes>
|
||||||
|
</PackageReleaseNotes>
|
||||||
|
|
||||||
|
<Version>3.0.0</Version>
|
||||||
|
<AssemblyVersion>3.0.0</AssemblyVersion>
|
||||||
|
<FileVersion>3.0.0</FileVersion>
|
||||||
|
|
||||||
|
<OutputPath>..\bin\</OutputPath>
|
||||||
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
|
<IncludeSource>True</IncludeSource>
|
||||||
|
<IncludeSymbols>True</IncludeSymbols>
|
||||||
|
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)'=='Debug'">
|
||||||
|
<DefineConstants>$(DefineConstants);TRACE;DEBUG</DefineConstants>
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
<Optimize>false</Optimize>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
|
||||||
|
<DebugType>portable</DebugType>
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<NoWarn>$(NoWarn);CS1591;CS1572;CS1573</NoWarn>
|
||||||
|
<DefineConstants>$(DefineConstants);RELEASE</DefineConstants>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0-windows'">
|
||||||
|
<Using Include="ScreenCapture.NET.Compatibility.Net8" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="Resources\icon.png">
|
||||||
|
<Pack>True</Pack>
|
||||||
|
<PackagePath></PackagePath>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="HPPH.System.Drawing" Version="1.0.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\ScreenCapture.NET\ScreenCapture.NET.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@ -15,6 +15,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ScreenCapture.NET.DX9", "Sc
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ScreenCapture.NET.X11", "ScreenCapture.NET.X11\ScreenCapture.NET.X11.csproj", "{F81562C8-2035-4FB9-9547-C51F9D343BDF}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ScreenCapture.NET.X11", "ScreenCapture.NET.X11\ScreenCapture.NET.X11.csproj", "{F81562C8-2035-4FB9-9547-C51F9D343BDF}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ScreenCapture.NET.GDI", "ScreenCapture.NET.GDI\ScreenCapture.NET.GDI.csproj", "{53FEEA34-B9F0-4A02-9408-BCC3AD0EC1E3}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@ -41,6 +43,10 @@ Global
|
|||||||
{F81562C8-2035-4FB9-9547-C51F9D343BDF}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{F81562C8-2035-4FB9-9547-C51F9D343BDF}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{F81562C8-2035-4FB9-9547-C51F9D343BDF}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{F81562C8-2035-4FB9-9547-C51F9D343BDF}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{F81562C8-2035-4FB9-9547-C51F9D343BDF}.Release|Any CPU.Build.0 = Release|Any CPU
|
{F81562C8-2035-4FB9-9547-C51F9D343BDF}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{53FEEA34-B9F0-4A02-9408-BCC3AD0EC1E3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{53FEEA34-B9F0-4A02-9408-BCC3AD0EC1E3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{53FEEA34-B9F0-4A02-9408-BCC3AD0EC1E3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{53FEEA34-B9F0-4A02-9408-BCC3AD0EC1E3}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user