1

Is it possible (and how) to adjust the blending mode used to display a WPF form on the desktop?

I have a window that serves as an overlay for the entire screen. Here's the XAML:

<Window x:Class="RedGreenBarsWPF.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Red/Green Overlay" Topmost="True" Height="300" Width="525" AllowsTransparency="True" WindowStyle="None" Background="Transparent" WindowStartupLocation="Manual" IsHitTestVisible="False">
    <Canvas Name="canvas" />
</Window>

It can't be clicked, and it gets resized and moved to cover the entire screen when the Window loads. I then draw some shapes on the canvas like this:

System.Windows.Media.Brush red = new SolidColorBrush(System.Windows.Media.Color.FromArgb(200, 255, 0, 0));

System.Windows.Size s = new System.Windows.Size(System.Windows.SystemParameters.PrimaryScreenWidth, System.Windows.SystemParameters.PrimaryScreenHeight);

int lines = 20;
for (double i = 0; i < s.Width; i += s.Width / lines)
{
    System.Windows.Shapes.Rectangle rect = new System.Windows.Shapes.Rectangle
    {
        Width = s.Width / lines,
        Height = s.Height
    };

    rect.Fill = red;

    Canvas.SetTop(rect, 0);
    Canvas.SetLeft(rect, i * 2);
    canvas.Children.Add(rect);
}

This does exactly what it should, but not what I want. Here's a visualization done in Photoshop (mine looks like "Normal"):

enter image description here

I need to figure out a way to make it look like the red box on the right, where the text isn't lightened by the overlaying colour. I've searched high and low, and although there are libraries out there that can accomplish this with elements within the Window, I need the blend mode to extend over the entire desktop. How can this be done?

Scott
  • 5,338
  • 5
  • 45
  • 70
  • What is your algorithm for combining the overlay and the underlying image? The image on the left has the red portion with some level of transparency and the black and the red merge together to give you the "lightened" appearance. The mock up on the right seems to be replace all white with red, but leave the black alone. This works fine with a binary black and white image, but what happens when the text isn't black or the background isn't white? – John Koerner Apr 16 '14 at 02:03
  • I was trying to experiment with this myself...My thinking was in order to do something like this, you'd have to get the pixel data in each rectangle, and if the data wasn't exactly R = 200 (or whatever red value you specify), you make that pixel completely transparent. So red would show up as red on completely white background, but makes everything else bleed through. However, it seems there's no good way of doing this with WPF (at least I haven't gotten it right yet..) – ryrich Apr 16 '14 at 03:14
  • @JohnKoerner I'm not using any kind of specific algorithm to draw the rectangle, I'm just adding it directly to the canvas. The text appears behind it because the alpha value is set to only 200 (255 being fully opaque). – Scott Apr 16 '14 at 03:49
  • @ryrich My program allows users to change what colour they want the box(s) to be. I was looking into getting the colours of each pixel beneath the rectangle, but it ended up being highly inefficient. I also thought about looking into the various game frameworks that MS offers, even, but I doubt they include desktop compositing/blending... ah well. – Scott Apr 16 '14 at 03:51

1 Answers1

1

You need to use a different blending function. So far I believe you have no easy solution ; this has already been requested but is not yet supported by WPF.

This other question was also about using different blending functions for WPF brushes. It also contains links to a pretty nice (but long) tutorial about how implementing custom blending (it's a whole blending library actually).

These pages contain zips with the source code on almost each page. You need the following to build them: (quoting) .NET 3.5 SP1, DirectX SDK, and the Shader Effects BuildTask and Templates from the WPF Futures stuff on CodePlex.

I'm copying the links here:

EDIT: To capture the desktop image, you can try to use a simple FrameworkElement, such as a Rectangle, with a transparent background (Transparent is a brush). Then you can use the RenderTargetBitmap class to convert it to an ImageBrush. Here is a code snippet that should help you to get started:

public Brush RectangleToBrush(Rectangle rect)
{
    int w = (int)rect.ActualWidth;
    int h = (int)rect.ActualHeight;
    var rtb = new RenderTargetBitmap(w, h, 96d, 96d, PixelFormats.Default);
    rtb.Render(rect);

    ImageBrush brush = new ImageBrush(BitmapFrame.Create(rtb));

    return brush;
}

You can probably put this rectangle in your .xaml file, using a Grid, to make it cover the whole window:

<Window ...>
    <Grid>
        <Rectangle x:Name="DesktopCaptureRectangle"/>
        <Grid>
            <!-- Your controls here -->
        </Grid>
    </Grid>
</Window>

Note: I'm not sure this solution will be good performance-wise...

Community
  • 1
  • 1
Benlitz
  • 1,952
  • 1
  • 17
  • 30
  • Looks good! Is there any way I can get the desktop itself as an input for the multi-input shader? – Scott Apr 16 '14 at 15:43
  • 1
    I edited my answer with an idea about how to do that... I didn't actually tested it so I'm not sure it will work, but you can give it a try. – Benlitz Apr 17 '14 at 02:54
  • Let us know if you succeed to do it, this was an interesting question and it would be interesting and useful to have the results here! – Benlitz Apr 18 '14 at 02:21
  • I tested the rectangle with transparent background but the result Brush is Null... – Ramin Bateni Aug 22 '15 at 03:05