mirror of
https://github.com/DarthAffe/ScreenCapture.NET.git
synced 2025-12-12 13:28:35 +00:00
175 lines
5.1 KiB
C#
175 lines
5.1 KiB
C#
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
|
|
} |