Click here to Skip to main content
12,078,489 members (46,320 online)
Click here to Skip to main content
Add your own
alternative version

Tagged as


131 bookmarked

Very fast screen capture using DirectX in C#

, 27 Oct 2011 CPOL
Rate this:
Please Sign up or sign in to vote.
Traditionally screen captures are obtained by GDI, but it's slow. In my homemade ambilight project, I needed a fast printscreen and it's posibble: DirectX device has a front buffer - direct image of the screen.


Recently I programmed my new project - homemade ambilight. Ambilight is a backlight behind television. The light is the average of some pixels in the screen. In order to get the colors for the ambilight, I needed fast screen capture. After some search, I heard about the front buffer. DirectX devices have this cool property. It contains the actual screen image. It is faster than GDI. DirectX puts an image in the surface object and it's faster for processing than GDI's bitmap.

Requirements and project preparing

Your project must be STAThread. First you need the DirectX SDK. It contains all the libraries that you need. When you have it downloaded and installed, add to your project the following references:

  • Microsoft.DirectX
  • Microsoft.DirectX.Direct3D
  • Microsoft.DirectX.Direct3DX

If you can't find these in the list of references, then look for these libraries in "C:\Windows\Microsoft.NET\DirectX for Managed Code" and "C:\Windows\Microsoft.NET".

In order to get Direct3D working, we have to add these lines into the app.config (if it doesn't exist, add a new file):

<startup useLegacyV2RuntimeActivationPolicy="true">
  <supportedRuntime version="v4.0" 

In the supportedRuntime tag, change the version to the .NET version that you use.

A class for capturing screen

Our new class DxScreenCapture will have functions for capturing screen. In order to get the front buffer, we need a DirectX device. It can be created from Form or another Control. So our class must inherit from Form. Next, declare the device (of course, add using statements for DirectX too).

public class DxScreenCapture : Form
    Device d;

Next, let's setup the device.

public DxScreenCapture()
    PresentParameters present_params = new PresentParameters();
    present_params.Windowed = true;
    present_params.SwapEffect = SwapEffect.Discard;
    d = new Device(0, DeviceType.Hardware, this, 
            CreateFlags.SoftwareVertexProcessing, present_params);

The device renders images with hardware and processes vertexes by software. It's irrelevant for our project. Now we can access the front buffer! This is the method for getting the print screen:

public Surface CaptureScreen()
    Surface s = d.CreateOffscreenPlainSurface(Screen.PrimaryScreen.Bounds.Width, 
                Screen.PrimaryScreen.Bounds.Height, Format.A8R8G8B8, Pool.Scratch);
    d.GetFrontBufferData(0, s);
    return s;

Surface is a DirectX type of image. We can convert this to Bitmap, but it takes up much time. Locking the pixels of the surface is fast, so for processing it is OK.

First, the method creates a new surface. Next, we get the front buffer from the device to the surface and then return the surface. The class for capturing is ready. Isn't this easy?

Examples of usage

This method is fast, but if you save images to the hard drive, it takes much time. It isn't the best way to record the video of the screen. If you want to get a screen capture, you can use normal print screen from Graphics.

Saving and viewing are slow, but capturing is fast. So if you have an application that needs some pixels or average, this is the best way: simple and fast.

This solution I found when I wrote my homemade ambilight driver. This project needed the average of colors of the screen's edges and it had to refresh a minimum of ten times per second. Maybe, GDI would have sufficed, but it charged the system.

The example "Colors average" is a part of my ambilight. It works very fast. First, I calculate the positions of pixels in the locked pixels stream:

Collection<long> tlPos = new Collection<long>();
Collection<long> tPos = new Collection<long>();
Collection<long> trPos = new Collection<long>();
Collection<long> lPos = new Collection<long>();
Collection<long> rPos = new Collection<long>();
Collection<long> blPos = new Collection<long>();
Collection<long> bPos = new Collection<long>();
Collection<long> brPos = new Collection<long>();
int o = 20;
int m = 8;
int sx = Screen.PrimaryScreen.Bounds.Width - m;
int sy = Screen.PrimaryScreen.Bounds.Height - m;
int bx = (sx - m) / 3 + m;
int by = (sy - m) / 3 + m;
int bx2 = (sx - m) * 2 / 3 + m;
int by2 = (sy - m) * 2 / 3 + m;

long x, y;
long pos;

y = m;
for (x = m; x < sx; x += o)
    pos = (y * Screen.PrimaryScreen.Bounds.Width + x) * Bpp;
    if (x < bx)
    else if (x > bx && x < bx2)
    else if (x > bx2)

y = sy;
for (x = m; x < sx; x += o)
    pos = (y * Screen.PrimaryScreen.Bounds.Width + x) * Bpp;
    if (x < bx)
    else if (x > bx && x < bx2)
    else if (x > bx2)

x = m;
for (y = m + 1; y < sy - 1; y += o)
    pos = (y * Screen.PrimaryScreen.Bounds.Width + x) * Bpp;
    if (y < by)
    else if (y > by && y < by2)
    else if (y > by2)

x = sx;
for (y = m + 1; y < sy - 1; y += o)
    pos = (y * Screen.PrimaryScreen.Bounds.Width + x) * Bpp;
    if (y < by)
    else if (y > by && y < by2)
    else if (y > by2)

I created a Calculate method and I raise it with each timer tick. I capture the screen and lock pixels. Locking pixels is converting the surface or bitmap to a stream with pure pixel values. To read the stream, you must know its width and in which format it is saved. In DirectX, the format is specified when the surface is created. In the CaptureScreen method, there is:

Surface s = d.CreateOffscreenPlainSurface(Screen.PrimaryScreen.Bounds.Width, 
            Screen.PrimaryScreen.Bounds.Height, Format.A8R8G8B8, Pool.Scratch);

A8R8G8B8 is a 32-bit RGB format, where each pixel has one byte for alpha, one for red, one for green, and one byte for blue. In the stream, the first four bytes are the first pixel, next 4 bytes are the second pixel, and so on. So in the Calculate method, I wrote:

Surface s = sc.CaptureScreen();
GraphicsStream gs = s.LockRectangle(LockFlags.None);

Next, I wrote the avcs method that reads the pixels specified in the table containing their locations and returns their average.

Color avcs(GraphicsStream gs, Collection<long> positions)
    byte[] bu = new byte[4];
    int r = 0;
    int g = 0;
    int b = 0;
    int i = 0;

    foreach (long pos in positions)
        gs.Position = pos;
        gs.Read(bu, 0, 4);
        r += bu[2];
        g += bu[1];
        b += bu[0];

    return Color.FromArgb(r / i, g / i, b / i);

Finally, I set the colors to preview and dispose the objects:

topLeft.BackColor = avcs(gs, tlPos);
topRight.BackColor = avcs(gs, trPos);
bottomLeft.BackColor = avcs(gs, blPos);
bottomRight.BackColor = avcs(gs, brPos);

top.BackColor = avcs(gs, tPos);
bottom.BackColor = avcs(gs, bPos);
left.BackColor = avcs(gs, lPos);
right.BackColor = avcs(gs, rPos);


If you run some video behind the example's window, you can see how fast this method is.

You can use the DirectX solution for all screen processing problems, if you don't view or save full image.


The Print Screen button captures the screen by GDI. This slow method is wrapped in System.Drawing. In order to get fast print screens for processing, not for saving or viewing, DirectX is a better solution than GDI. A DirectX device has a front buffer which contains the rendered screen.


This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


About the Author

You may also be interested in...

Comments and Discussions

SuggestionInteresting Article Pin
dburr31-Oct-11 14:40
memberdburr31-Oct-11 14:40 
GeneralRe: Interesting Article Pin
WhitW11-Nov-12 14:44
memberWhitW11-Nov-12 14:44 
GeneralRe: Interesting Article Pin
dburr11-Nov-12 15:06
memberdburr11-Nov-12 15:06 
Questiongood job Pin
Slacker00728-Oct-11 2:18
memberSlacker00728-Oct-11 2:18 
SuggestionAverage? Pin
Julien Villers28-Oct-11 0:15
memberJulien Villers28-Oct-11 0:15 
GeneralRe: Average? Pin
Milfje11-Dec-12 3:24
memberMilfje11-Dec-12 3:24 
GeneralRe: Average? Pin
Julien Villers11-Dec-12 7:03
memberJulien Villers11-Dec-12 7:03 
GeneralMy vote of 5 Pin
GPUToaster™27-Oct-11 21:09
memberGPUToaster™27-Oct-11 21:09 
Explanation flow was good. Keep it up!

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.160212.1 | Last Updated 27 Oct 2011
Article Copyright 2011 by TeapotDev
Everything else Copyright © CodeProject, 1999-2016
Layout: fixed | fluid