Question

I have a Path. It displays correctly in a ContentControl:

<ContentControl
    Name="PathContainer"
    Content="{Binding Path}"
    RenderTransform="{StaticResource ScaleTransform}"
    Width="Auto"
    Height="Auto"
    Background="Transparent"
    />

But I'd like to copy it to the clipboard. The following creates what appears to be a solid black bitmap of the correct size. If I change the brush to White, it creates a solid white bitmap of the correct size. If, instead of creating this Canvas, I render from the actual ContentControl which hosts the Path in the UI, I get a bitmap of the correct size, with the Path drawn on it, but with what appears to be an opaque black background (unless I change the background to PapayaWhip or something, which does what you'd expect).

When I say "appears to be an opaque black background", I mean that when I copy it to the clipboard with System.Windows.Clipboard.CopyImage() and paste it into the VS icon or bitmap editors, the background of the pasted image is black rather than transparent, and the anti-aliased pixels that ought to be semi-transparent are the color they'd appear to be on a black background.

So, two questions:

First: Is the following code broken, or am I just trying to do something you can't do? It doesn't throw any exceptions, FWIW in WPF.

Second: How do you make a bitmap with an alpha channel in WPF? Should I quit wasting my time on all these labor-saving abstractions, whack up a BITMAPINFOHEADER, and create the ARGB quads in a loop by hand?

public BitmapSource CreateImage(Transform tfm)
{
    var path = CreatePath();

    var rcBounds = path.Data.GetRenderBounds(GetPen());

    if (null != tfm)
    {
        rcBounds = tfm.TransformBounds(rcBounds);
    }

    int pxWidth = (int)Math.Round(rcBounds.Width);
    int pxHeight = (int)Math.Round(rcBounds.Height);

    var canvas = new System.Windows.Controls.Canvas();

    canvas.Width = pxWidth;
    canvas.Height = pxHeight;
    canvas.Margin = new System.Windows.Thickness(0);
    canvas.Background = Brushes.Transparent;

    canvas.Measure(new Size(canvas.Width, canvas.Height));
    canvas.Arrange(new Rect(new Size(canvas.Width, canvas.Height)));
    canvas.UpdateLayout();

    canvas.Children.Add(path);

    canvas.RenderTransform = tfm;

    RenderTargetBitmap rtb = new RenderTargetBitmap(pxWidth, pxHeight, 
                                     96, 96, PixelFormats.Pbgra32);

    rtb.Render(canvas);

    return BitmapFrame.Create(rtb);
}
Was it helpful?

Solution

There seems to be no need for the Canvas. You could directly render the Path into the RenderTargetBitmap, like e.g. the following blue circle with transparent background:

var path = new Path
{
    Data = new EllipseGeometry(new Point(100, 100), 100, 100),
    Fill = Brushes.Blue
};

var bounds = path.Data.GetRenderBounds(null);
path.Measure(bounds.Size);
path.Arrange(bounds);

var bitmap = new RenderTargetBitmap(
    (int)bounds.Width, (int)bounds.Height, 96, 96, PixelFormats.Pbgra32);
bitmap.Render(path);
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top