using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using SharpDX; using SharpDX.Direct3D11; using SharpDX.DXGI; using Device = SharpDX.Direct3D11.Device; using MapFlags = SharpDX.Direct3D11.MapFlags; namespace Tango.ScreenCapture { public class DirectXScreenCapture : IScreenCaptureMethod { private static bool _hasInstance; private Device device; private Output1 output1; private Texture2DDescription textureDesc; private OutputDuplication duplicatedOutput; private int monitorWidth; private int monitorHeight; public int AdapterIndex { get; set; } public int MonitorIndex { get; set; } public DirectXScreenCapture() { if (_hasInstance) { throw new InvalidOperationException("An instance of the DirectX screen capture method already exists. Please dispose it before attempting to create a new one."); } // Create DXGI Factory1 var factory = new Factory1(); var adapter = factory.GetAdapter1(AdapterIndex); // Create device from Adapter device = new Device(adapter); // Get DXGI.Output var output = adapter.GetOutput(MonitorIndex); output1 = output.QueryInterface(); // Width/Height of desktop to capture monitorWidth = ((SharpDX.Rectangle)output.Description.DesktopBounds).Width; monitorHeight = ((SharpDX.Rectangle)output.Description.DesktopBounds).Height; // Create Staging texture CPU-accessible textureDesc = new Texture2DDescription { CpuAccessFlags = CpuAccessFlags.Read, BindFlags = BindFlags.None, Format = Format.B8G8R8A8_UNorm, Width = monitorWidth, Height = monitorHeight, OptionFlags = ResourceOptionFlags.None, MipLevels = 1, ArraySize = 1, SampleDescription = { Count = 1, Quality = 0 }, Usage = ResourceUsage.Staging }; // Duplicate the output duplicatedOutput = output1.DuplicateOutput(device); _hasInstance = true; } public virtual Bitmap GetDesktopBitmap(CaptureRegion region) { var screenTexture = new Texture2D(device, textureDesc); SharpDX.DXGI.Resource screenResource; OutputDuplicateFrameInformation duplicateFrameInformation; // Try to get duplicated frame within given time duplicatedOutput.AcquireNextFrame(10000, out duplicateFrameInformation, out screenResource); // copy resource into memory that can be accessed by the CPU using (var screenTexture2D = screenResource.QueryInterface()) device.ImmediateContext.CopyResource(screenTexture2D, screenTexture); // Get the desktop capture texture var mapSource = device.ImmediateContext.MapSubresource(screenTexture, 0, MapMode.Read, MapFlags.None); // Create Drawing.Bitmap var bitmap = new Bitmap(monitorWidth, monitorHeight, System.Drawing.Imaging.PixelFormat.Format32bppRgb); var boundsRect = new System.Drawing.Rectangle(0, 0, monitorWidth, monitorHeight); // Copy pixels from screen capture Texture to GDI bitmap var mapDest = bitmap.LockBits(boundsRect, System.Drawing.Imaging.ImageLockMode.WriteOnly, bitmap.PixelFormat); var sourcePtr = mapSource.DataPointer; var destPtr = mapDest.Scan0; for (int y = 0; y < region.Height; y++) { // Copy a single line Utilities.CopyMemory(destPtr, sourcePtr, monitorWidth * 4); // Advance pointers sourcePtr = IntPtr.Add(sourcePtr, mapSource.RowPitch); destPtr = IntPtr.Add(destPtr, mapDest.Stride); } // Release source and dest locks bitmap.UnlockBits(mapDest); device.ImmediateContext.UnmapSubresource(screenTexture, 0); if (region.Left > 0 || region.Top > 0 || region.Width < monitorWidth || region.Height < monitorHeight) { var cropped = CropImage(bitmap, new System.Drawing.Rectangle(region.Left, region.Top, region.Width, region.Height)); bitmap.Dispose(); bitmap = cropped; } screenTexture.Dispose(); screenResource.Dispose(); duplicatedOutput.ReleaseFrame(); return bitmap; } private Bitmap CropImage(Bitmap img, System.Drawing.Rectangle cropArea) { Bitmap bmpImage = new Bitmap(img); return bmpImage.Clone(cropArea, bmpImage.PixelFormat); } public void Dispose() { _hasInstance = false; duplicatedOutput.Dispose(); device.Dispose(); } } }