C# Which is the fastest way to take a screen shot?

后端 未结 3 1537
再見小時候
再見小時候 2020-12-09 06:06

I am implementing a feature that will take screen shot repeatedly and output dirty rectangles between 2 different shots then send re-draw the screen in a window.

I c

相关标签:
3条回答
  • 2020-12-09 06:16

    For those who come to this thread, I came to this solution :

    using SharpDX;
    using SharpDX.Direct3D11;
    using SharpDX.DXGI;
    using System;
    using System.Diagnostics;
    using System.Drawing;
    using System.Drawing.Imaging;
    using System.IO;
    using System.Threading.Tasks;
    

    You will need the package SharpDX and SharpDX.Direct3D11

    public class ScreenStateLogger
    {
        private byte[] _previousScreen;
        private bool _run, _init;
    
        public int Size { get; private set; }
        public ScreenStateLogger()
        {
    
        }
    
        public void Start()
        {
            _run = true;
            var factory = new Factory1();
            //Get first adapter
            var adapter = factory.GetAdapter1(0);
            //Get device from adapter
            var device = new SharpDX.Direct3D11.Device(adapter);
            //Get front buffer of the adapter
            var output = adapter.GetOutput(0);
            var output1 = output.QueryInterface<Output1>();
    
            // Width/Height of desktop to capture
            int width = output.Description.DesktopBounds.Right;
            int height = output.Description.DesktopBounds.Bottom;
    
            // Create Staging texture CPU-accessible
            var textureDesc = new Texture2DDescription
            {
                CpuAccessFlags = CpuAccessFlags.Read,
                BindFlags = BindFlags.None,
                Format = Format.B8G8R8A8_UNorm,
                Width = width,
                Height = height,
                OptionFlags = ResourceOptionFlags.None,
                MipLevels = 1,
                ArraySize = 1,
                SampleDescription = { Count = 1, Quality = 0 },
                Usage = ResourceUsage.Staging
            };
            var screenTexture = new Texture2D(device, textureDesc);
    
            Task.Factory.StartNew(() =>
            {
                // Duplicate the output
                using (var duplicatedOutput = output1.DuplicateOutput(device))
                {
                    while (_run)
                    {
                        try
                        {
                            SharpDX.DXGI.Resource screenResource;
                            OutputDuplicateFrameInformation duplicateFrameInformation;
    
                            // Try to get duplicated frame within given time is ms
                            duplicatedOutput.AcquireNextFrame(5, out duplicateFrameInformation, out screenResource);
    
                            // copy resource into memory that can be accessed by the CPU
                            using (var screenTexture2D = screenResource.QueryInterface<Texture2D>())
                                device.ImmediateContext.CopyResource(screenTexture2D, screenTexture);
    
                            // Get the desktop capture texture
                            var mapSource = device.ImmediateContext.MapSubresource(screenTexture, 0, MapMode.Read, SharpDX.Direct3D11.MapFlags.None);
    
                            // Create Drawing.Bitmap
                            using (var bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb))
                            {
                                var boundsRect = new Rectangle(0, 0, width, height);
    
                                // Copy pixels from screen capture Texture to GDI bitmap
                                var mapDest = bitmap.LockBits(boundsRect, ImageLockMode.WriteOnly, bitmap.PixelFormat);
                                var sourcePtr = mapSource.DataPointer;
                                var destPtr = mapDest.Scan0;
                                for (int y = 0; y < height; y++)
                                {
                                    // Copy a single line 
                                    Utilities.CopyMemory(destPtr, sourcePtr, width * 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);
    
                                using (var ms = new MemoryStream())
                                {
                                    bitmap.Save(ms, ImageFormat.Bmp);
                                    ScreenRefreshed?.Invoke(this, ms.ToArray());
                                    _init = true;
                                }
                            }
                            screenResource.Dispose();
                            duplicatedOutput.ReleaseFrame();
                        }
                        catch (SharpDXException e)
                        {
                            if (e.ResultCode.Code != SharpDX.DXGI.ResultCode.WaitTimeout.Result.Code)
                            {
                                Trace.TraceError(e.Message);
                                Trace.TraceError(e.StackTrace);
                            }
                        }
                    }
                }
            });
            while (!_init) ;
        }
    
        public void Stop()
        {
            _run = false;
        }
    
        public EventHandler<byte[]> ScreenRefreshed;
    }
    

    This code will get as fast as possible the frames from the front buffer of the graphic device and retrieve the byte[] from the bitmap it creates. The code seems to be stable in memory and processor usage (GPU and CPU).

    usage :

    var screenStateLogger = new ScreenStateLogger();
    screenStateLogger.ScreenRefreshed += (sender, data) =>
    {
        //New frame in data
    };
    screenStateLogger.Start();
    
    0 讨论(0)
  • 2020-12-09 06:17

    For alternative to CopyFromScreen() check this. Note that the Graphics.CopyFromScreen() itself calls the API BitBlt() to copy from screen, you can check the source code using Reflector.

    0 讨论(0)
  • 2020-12-09 06:32

    This is very similar to a question asked several years ago: Here. That question was whether directx's capturing abilities could be used to get better performance.

    The consensus was that it probably wouldn't provide any performance increase, TightVNC does it very quickly by cheating. It uses a driver that doesn't have to use the API that (presumably) .NET is using.

    At some point I recall looking at the source code for Camstudio and I believe they use directx's capturing capabilities. I don't think you can push that much past 30 fps, and most of the time not even that. I'm not sure if that is a problem with the hooks camstudio uses to figure out when something has changed or the actual capture mechanism.

    0 讨论(0)
提交回复
热议问题