
Introduction
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"
sku=".NETFramework,Version=v4.0,Profile=Client"/>
</startup>
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)
tlPos.Add(pos);
else if (x > bx && x < bx2)
tPos.Add(pos);
else if (x > bx2)
trPos.Add(pos);
}
y = sy;
for (x = m; x < sx; x += o)
{
pos = (y * Screen.PrimaryScreen.Bounds.Width + x) * Bpp;
if (x < bx)
blPos.Add(pos);
else if (x > bx && x < bx2)
bPos.Add(pos);
else if (x > bx2)
brPos.Add(pos);
}
x = m;
for (y = m + 1; y < sy - 1; y += o)
{
pos = (y * Screen.PrimaryScreen.Bounds.Width + x) * Bpp;
if (y < by)
tlPos.Add(pos);
else if (y > by && y < by2)
lPos.Add(pos);
else if (y > by2)
blPos.Add(pos);
}
x = sx;
for (y = m + 1; y < sy - 1; y += o)
{
pos = (y * Screen.PrimaryScreen.Bounds.Width + x) * Bpp;
if (y < by)
trPos.Add(pos);
else if (y > by && y < by2)
rPos.Add(pos);
else if (y > by2)
brPos.Add(pos);
}
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];
i++;
}
return Color.FromArgb(r / i, g / i, b / i);
}</long>
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);
gs.Close();
gs.Dispose();
s.UnlockRectangle();
s.ReleaseGraphics();
s.Dispose();
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.
Conclusion
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.