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
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ScreenCapture.NET.X11", "ScreenCapture.NET.X11\ScreenCapture.NET.X11.csproj", "{F81562C8-2035-4FB9-9547-C51F9D343BDF}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ScreenCapture.NET.GDI", "ScreenCapture.NET.GDI\ScreenCapture.NET.GDI.csproj", "{53FEEA34-B9F0-4A02-9408-BCC3AD0EC1E3}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
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}.Release|Any CPU.ActiveCfg = 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
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user