Skip to content

DarthAffe/ScreenCapture.NET

Repository files navigation

ScreenCapture.NET

GitHub release (latest by date) Nuget GitHub GitHub Repo stars

NuGet-Packages:

Package Description
ScreenCapture.NET The core-package required to use ScreenCapture.NET captures or write your own.
ScreenCapture.NET.DX11 DirectX 11 based capturing. Fast and supports the whole set of features. This should always be used if possible! Note: This might not work on windows hybrid systems (check the docs and this issue)
ScreenCapture.NET.DX9 DirectX 9 based capturing. Slower then DX 11 and does not support rotated screens and GPU-accelerated downscaling. Only useful if the DX11 package can't be used for some reason.
ScreenCapture.NET.X11 libX11 based capturing for the X-Window-System. Currently the only way to use ScreenCapture.NET on linux. Quite slow and can easily break depending on the X-Server config. Works on my machine, but it's not really a high proprity to support at the moment. Does not support rotated screens and GPU-accelerated downscaling.

Usage

// Create a screen-capture service
IScreenCaptureService screenCaptureService = new DX11ScreenCaptureService();

// Get all available graphics cards
IEnumerable<GraphicsCard> graphicsCards = screenCaptureService.GetGraphicsCards();

// Get the displays from the graphics card(s) you are interested in
IEnumerable<Display> displays = screenCaptureService.GetDisplays(graphicsCards.First());

// Create a screen-capture for all screens you want to capture
IScreenCapture screenCapture = screenCaptureService.GetScreenCapture(displays.First());

// Register the regions you want to capture on the screen
// Capture the whole screen
ICaptureZone fullscreen = screenCapture.RegisterCaptureZone(0, 0, screenCapture.Display.Width, screenCapture.Display.Height);
// Capture a 100x100 region at the top left and scale it down to 50x50
ICaptureZone topLeft = screenCapture.RegisterCaptureZone(0, 0, 100, 100, downscaleLevel: 1);

// Capture the screen
// This should be done in a loop on a separate thread as CaptureScreen blocks if the screen is not updated (still image).
screenCapture.CaptureScreen();

// Do something with the captured image - e.g. access all pixels (same could be done with topLeft)

//Lock the zone to access the data. Remember to dispose the returned disposable to unlock again.
using (fullscreen.Lock())
{
    // You have multiple options now:
    // 1. Access the raw byte-data
    ReadOnlySpan<byte> rawData = fullscreen.RawBuffer;

    // 2. Use the provided abstraction to access pixels without having to care about low-level byte handling
    // Get the image captured for the zone
    IImage image = fullscreen.Image;

    // Iterate all pixels of the image
    foreach (IColor color in image)
        Console.WriteLine($"A: {color.A}, R: {color.R}, G: {color.G}, B: {color.B}");

    // Get the pixel at location (x = 10, y = 20)
    IColor imageColorExample = image[10, 20];

    // Get the first row
    IImageRow row = image.Rows[0];
    // Get the 10th pixel of the row
    IColor rowColorExample = row[10];

    // Get the first column
    IImageColumn column = image.Columns[0];
    // Get the 10th pixel of the column
    IColor columnColorExample = column[10];

    // Cuts a rectangle out of the original image (x = 100, y = 150, width = 400, height = 300)
    IImage subImage = image[100, 150, 400, 300];

    // All of the things above (rows, columns, sub-images) do NOT allocate new memory so they are fast and memory efficient, but for that reason don't provide raw byte access.
}

If you know which Capture-provider you're using it performs a bit better to not use the abstraction but a more low-level approach instead.
This is the same example as above but without using the interfaces:

DX11ScreenCaptureService screenCaptureService = new DX11ScreenCaptureService();
IEnumerable<GraphicsCard> graphicsCards = screenCaptureService.GetGraphicsCards();
IEnumerable<Display> displays = screenCaptureService.GetDisplays(graphicsCards.First());
DX11ScreenCapture screenCapture = screenCaptureService.GetScreenCapture(displays.First());

CaptureZone<ColorBGRA> fullscreen = screenCapture.RegisterCaptureZone(0, 0, screenCapture.Display.Width, screenCapture.Display.Height);
CaptureZone<ColorBGRA> topLeft = screenCapture.RegisterCaptureZone(0, 0, 100, 100, downscaleLevel: 1);

screenCapture.CaptureScreen();

using (fullscreen.Lock())
{
    IImage<ColorBGRA> image = fullscreen.Image;

    // You can also get a ref image which has a slight performance benefit in some cases
    // RefImage<ColorBGRA> refImage = image.AsRefImage();

    foreach (ColorBGRA color in image)
        Console.WriteLine($"A: {color.A}, R: {color.R}, G: {color.G}, B: {color.B}");

    ColorBGRA imageColorExample = image[10, 20];

    ImageRow<ColorBGRA> row = image.Rows[0];
    ColorBGRA rowColorExample = row[10];

    ImageColumn<ColorBGRA> column = image.Columns[0];
    ColorBGRA columnColorExample = column[10];

    RefImage<ColorBGRA> subImage = image[100, 150, 400, 300];
}