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; /// /// Represents a ScreenCapture using GDI /// // ReSharper disable once InconsistentNaming public sealed partial class GDIScreenCapture : AbstractScreenCapture { #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 /// /// Initializes a new instance of the class. /// /// The to duplicate. internal GDIScreenCapture(Display display) : base(display) { Restart(); } #endregion #region Methods /// 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; } /// protected override void PerformCaptureZoneUpdate(CaptureZone captureZone, Span 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 captureZone, Span buffer) { if (_bitmap == null) return; BitmapData data = _bitmap.LockBits(new Rectangle(0, 0, _bitmap.Width, _bitmap.Height), ImageLockMode.ReadOnly, _bitmap.PixelFormat); ReadOnlySpan bitmapBuffer = new(data.Scan0.ToPointer(), data.Stride * data.Height); RefImage.Wrap(bitmapBuffer, Display.Width, Display.Height, data.Stride)[captureZone.X, captureZone.Y, captureZone.Width, captureZone.Height] .CopyTo(MemoryMarshal.Cast(buffer)); _bitmap.UnlockBits(data); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private unsafe void DownscaleZone(CaptureZone captureZone, Span buffer) { BitmapData data = _bitmap.LockBits(new Rectangle(0, 0, _bitmap.Width, _bitmap.Height), ImageLockMode.ReadOnly, _bitmap.PixelFormat); ReadOnlySpan bitmapBuffer = new(data.Scan0.ToPointer(), data.Stride * data.Height); RefImage source = RefImage.Wrap(bitmapBuffer, Display.Width, Display.Height, data.Stride)[captureZone.X, captureZone.Y, captureZone.UnscaledWidth, captureZone.UnscaledHeight]; Span target = MemoryMarshal.Cast(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); } /// public override void Restart() { base.Restart(); lock (_captureLock) { _bitmap?.Dispose(); _bitmap = new Bitmap(Display.Width, Display.Height, PixelFormat.Format32bppArgb); } } /// protected override void Dispose(bool disposing) { base.Dispose(disposing); lock (_captureLock) DisposeDX(); } private void DisposeDX() { try { _bitmap?.Dispose(); _bitmap = null; } catch { /**/ } } #endregion }