Click here to Skip to main content
Click here to Skip to main content

Overlay Tools

, 5 Oct 2007
Rate this:
Please Sign up or sign in to vote.
..using DirectDraw - A tool for displaying a customisable overlay
Screenshot - render1.jpg Screenshot - render2.jpg
Screenshot - options1.jpg

Introduction

This tool allows you to display an overlay (which is over everything, including DirectX and your desktop (which means you can't make a screenshot of it directly)). By changing various settings, you can disable and enable several plugins and change their behavior.

Developers may create their own plugins to display their own stuff. Integrated in this tool are only some general plugins or plugins that I use.

Note: nVidia hardware only supports fragments of the DirectDraw overlay technique - Alpha won't work there (trying to set it will cause an error). If you have a nvidia card, I recommend using a smaller overlay with a light grey background color, or try using the FakeAlpha option instead, which will Blit the screen below it on the overlay. But please keep in mind that this is quite slow and needs a high render rate to be OK - This option is probably more useful if you put the overlay in a place where the background doesn't change often (e.g. over a GUI part).

Usage

Just start the OverlayTools.exe, and double click on the gear-tray-icon to display the options. Put your own settings there.

Standard Shortcuts:

  • F12 - Hide/Show
  • F11 - Switch overlay page

Background

I always wanted to see what others write to me in ICQ and mIRC while playing some fullscreen applications. It was annoying to hear the message-sound, but not know what was written. As overlays are a rather difficult and not a supported part of windows/DirectX, I tried creating something useful several times.

DirectDraw Overlays always had some problems with nVidia hardware, as nVidia hardware only supports YUV Overlays on which you cannot draw using GDI or anything else. Therefore I created my own blitting function for converting a RGB Bitmap (which works on every hardware) to a YUV surface.

1. Overlay Lib

1.1. Overlay Class

The overlay library offers the basic functions of displaying an overlay. To use it, just create an instance of the Overlay Class, set your Size and Position, Add your RenderDelegate, Initialise and Update() the overlay.

The first big problem here was the case of finding a surface pixelformat which would work on every hardware. ATI cards support mostly RGB, but also YUV; nVidia hardware only supports YUV. But as you cannot draw on YUV surfaces, it's quite useless. So I just try to find out which format works on the card and then use it.

do 
{
    try 
    {
        desc.PixelFormatStructure = GetPixelFormat(m_PixelFormat);
        m_Buffer = new Surface(desc, m_Device);
    }
    catch (DirectXException) 
    {
        m_PixelFormat++;
    }
} 
while (m_Buffer == null 
    && Enum.IsDefined(typeof(ePixelFormat), m_PixelFormat)); 

For rendering, I used a simple Bitmap as a render target as this would work everywhere, and then just blit it on the specific surface.

if (m_Renderer != null) 
{
    //Draw on the backbuffer
    Graphics g = Graphics.FromImage(m_RenderTarget);
    g.Clear(Color.Black);
    m_Renderer(g);
    Blit(m_RenderTarget, m_BackBuffer);
}

The blitting function just calls the sub-blitting function for the appropriate format:

public void Blit(Bitmap src, Surface dest) 
{
    switch (m_PixelFormat) 
    {
        case ePixelFormat.RGB32:
            BlitRGB32(src, dest);
            break;
        case ePixelFormat.YUY2:
            BlitYUY2(src, dest);
            break;
    }
}

The general way of blitting something from the source to the target is locking both data, then proceeding through it pixel by pixel (two-pixel by two-pixel in the YUY2 case)

BitmapData ds = src.LockBits(new Rectangle(0, 0, src.Width, src.Height), 
    ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
LockedData dd = dest.Lock(LockFlags.WriteOnly);

int ps = ds.Stride - (ds.Width*3); //the space left at the end of the Y-line
byte[] pd = new byte[dd.Pitch - (dd.Width*2)]; //dest space

byte* ptr = (byte*) ds.Scan0;

for (int h = 0; h < ds.Height; h++) 
{
    for (int w = 0; w < ds.Width; w += 2) 
    {
        ...
        ptr += 3;
        ...
    }
    //Skip the stride
    ptr += ps;
    pd.Write(pd, 0, pd.Length);
}

...

For RGB24 -> RGB32 blitting, I just lock the bitmap and copy the values, leaving alpha empty (as it is not available anyway, but a 24-bit surface won't create).

For RGB24 -> YUY2 blitting, I looked up the format on fourcc.org and tried to convert it properly. But as these formulas are not fully correct and I had to modify them a bit myself -the resulting colors may not be 100% identical.

1.2. KeyboardHook Class

For the shortcuts, I used the Win32 SetWindowHookEx method to hook up the WH_KEYBOARD_LL event. More information about that is available in the Windows Platform SDK.

...
hk = SetWindowsHookEx(13, hookprc, Marshal.GetHINSTANCE(GetType().Module), 0);
...
public IntPtr OnHook(int code, int wParam, int lParam) 
{
    IntPtr res = CallNextHookEx(hook, code, wParam, lParam);

    if (code >= 0 && m_Event != null && wParam == WM_KEYDOWN) 
    {
        KeyStruct s = (KeyStruct) Marshal.PtrToStructure(
                new IntPtr(lParam), typeof (KeyStruct));
        Keys key = (Keys) s.vkCode;
        m_Event(key);
    }

    return res;
}
... 

2. Overlay Tools

For displaying the custom overlays, I wrote this application. It handles the plugin management. Each plugin can render its own part to the overlay, so that in the end you get your fully customised overlay.

2.1. Loading the Plugins

The plugins are loaded via reflection. The code is searched and an instance is created for every inheritor of IPlugin.

foreach (Assembly a in AppDomain.CurrentDomain.GetAssemblies()) 
{
    foreach (Type t in a.GetTypes()) 
    {
        if (t.IsAbstract || t.IsInterface 
            || t.GetInterface(typeof (IPlugin).FullName) == null) 
        {
            continue;
        }

        //Get the plugin
        IPlugin p = (IPlugin) a.CreateInstance(t.FullName);

        ...
    }
}

Every plugin may contain data properties for saving custom values (e.g. Colors, Formats, ..). These values are signed by the [Save] Attribute, which is processed after creating the instance of the plugins. If a property possesses this attribute, its content is loaded from the .ini (using INIStreamer, one of my oldest C# classes) and then the string is converted to the appropriate type:

foreach (PropertyInfo i in t.GetProperties()) 
{
    if (i.GetCustomAttributes(typeof (SaveAttribute), true).Length == 0) 
    {
        continue;
    }
                            
    if (!tpc.Items.ContainsKey(i.Name)) 
    {
        continue; //no value for this property saved
    }

    object val;
    
    if (i.PropertyType.IsEnum) 
    { //Enums cant be converted
        val = Enum.Parse(i.PropertyType, tpc.Items[i.Name].ToString());
    }
    else 
    {
        val = Convert.ChangeType(tpc.Items[i.Name], i.PropertyType);
    }
    i.SetValue(p, val, null);
}

Nearly the same is done when saving the plugin data into the .ini file.

After loading the plugins, an options tab is generated for each plugin by adding the PluginPanel control (just a control with a propertygrid in fact) to the tab control.

private static void AddToTab(IPlugin p) 
{
    TabPage page = new TabPage();
    page.Text = p.Name;
    page.Name = "tab_plugin_" + p.BaseName;
    PluginPanel pp = new PluginPanel(p);
    pp.Dock = DockStyle.Fill;
    page.Controls.Add(pp);
    MainForm.tabs.TabPages.Add(page);
}

2.2. Managing the Plugins

All plugins have a OnTick() and OnRender(Graphics g) method. They are called by a timer for every active plugin, and so every plugin has a chance to fulfill its job. For the rendering, a TranslateTransform() Matrix-Operation is applied, so that the plugins can begin their rendering at (0|0).

public static void OnRender(Graphics g) 
{
    g.Clear(General.BackgroundColor);

    if (General.FakeAlpha) 
    {
        g.CopyFromScreen(Overlay.Boundings.Location, Point.Empty, 
            Overlay.Boundings.Size, CopyPixelOperation.SourceCopy);
    }
    
    foreach (IPlugin p in Plugins.Values) 
    {
        if (p.Active && (p.Page == General.Page || p.Page == -1)) 
        {
            g.ResetTransform();
            g.TranslateTransform(p.X, p.Y, MatrixOrder.Append);
            p.Render(g);
        }
    }
}

The plugins then just draw whatever they want (and maybe they use the Draw helper class, which has some fonts and draw strings with a shadow).

The general data like Position and Size of the overlay are also stored in a plugin: The General Plugin stores all important data in a fake/null-plugin (it has only an empty onTick/onRender method), which is saved in the same way normal plugins are saved.

2.3. List of Plugins

These are the plugins currently added to the program:

  1. Time Plugin - Displays the time
  2. CPU/Memory Plugins - Display the CPU Usage/Free memory
  3. Color Test Plugin - Just displays some different colors
  4. Message Plugin - Listens to an UDP Socket and displays messages if received
  5. ATITool Plugin - Reads the ATI Tool log file and displays the GPU Temperature

..and some smaller plugins which are not worth mentioning.

Creating Custom Plugins

If you want to create your own plugin, just add your class to the project file and put your stuff there. It's quite simple. I haven't added dynamic compilation/loading plugin assemblies as I think there is no need for this, but feel free to do so.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

]Metty[

Germany Germany
No Biography provided

Comments and Discussions

 
BugW7 64 bit crashing when Overlay is instantiated. Pinmemberemphero992-Jul-11 6:36 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    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 | Mobile
Web03 | 2.8.140709.1 | Last Updated 5 Oct 2007
Article Copyright 2007 by ]Metty[
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid