1

I've got a WPF User control with a canvas. It looks roughly like this (more complete version at the bottom):

<UserControl>
    <Canvas>
        <Image x:Name="CurrentImage" ... etc .../>
        <Path x:Name="SurfacePath" ... etc .../>
    </Canvas>
</UserControl>

Looks great on the screen but I need to write code that will create from all of this a new image "mask" of the same size as the original image. This mask will represent a combination of the two, with the original image pixels as black but the overlaid shape pixels as white. (A low-level SDK will later use the pixel data for some heavy math.)

Unfortunately, I am at a loss as to how to even approach creating this "mask" image. Aside from laying out elements in panels and building paths with geometries, the way that WPF drawing really works has always confused me. I get lost in all classes with similar names and when to use them, ImageDrawing vs DrawingImage, etc. I've read and googled and searched here quite a bit and I still don't know where to start.

Can anyone give me even the rough outline of the steps I would take to even approach this?

Here's an somewhat more complete version of what I'm using.

<UserControl x:Class="MyProject.Core.Shapes.ShapeCanvas"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             UseLayoutRounding="true"
             >
    <Canvas x:Name="Scene">

        <!--Image is drawn first, underneath everything else.  Image 0,0 is Canvas 0,0-->

        <Image x:Name="CurrentImage" 
               Canvas.Left="0" Canvas.Top="0"
               Source="{Binding Image, Mode=OneWay}"/>

        <!-- Path showing the current polyline created with the "Surface" tool. -->

        <Path x:Name="SurfacePath"
              ClipToBounds="False"
              Canvas.Left="0"
              Canvas.Top="0"
              Stroke="Gray"
              StrokeThickness="50" 
        >
            <Path.Data>
                <PathGeometry>
                    <PathGeometry.Figures>
                        <PathFigure StartPoint="{Binding CurrentSegmentStartPoint}">
                            <PathFigure.Segments>
                                <PolyBezierSegment Points="{Binding CurrentSegmentPoints}"/>
                            </PathFigure.Segments>
                        </PathFigure>
                    </PathGeometry.Figures>
                </PathGeometry>
            </Path.Data>
        </Path>
    </Canvas>
</UserControl>
Joe
  • 5,394
  • 3
  • 23
  • 54
  • What does "the original image pixels as black" exactly mean? All pixels black? Did you take a look at the WriteableBitmapEx library? – Clemens Jul 05 '18 at 16:43
  • I mean that any pixel in the new image that corresponds to a visible image pixel in the old one (i.e. an image pixel in the old one NOT covered by the Path) should end up as black in the new one. The idea here is that the user is defining in freehand an area of pixels that will be treated differently by the the low-level SDK. – Joe Jul 05 '18 at 16:53
  • 1
    Then draw just the path (with a white Stroke) into a RenderTargetBitmap. – Clemens Jul 05 '18 at 17:36

1 Answers1

1

I have used this code for rendering a WPF control to a BitmapSource/Image for code-behind:

    public static BitmapSource Create(UIElement control, Size size = default(Size))
    {
        if (control == null)
            return null;

        if (size == Size.Empty || (size.Height == 0 && size.Width == 0))
            size = new Size(double.PositiveInfinity, double.PositiveInfinity);

        control.Measure(size);
        RenderTargetBitmap bmp = new RenderTargetBitmap((int)control.DesiredSize.Width, (int)control.DesiredSize.Height, 96, 96, PixelFormats.Pbgra32);
        Rect rect = new Rect(0, 0, control.DesiredSize.Width, control.DesiredSize.Height);

        control.Arrange(rect);
        control.UpdateLayout();
        bmp.Render(control);
        bmp.Freeze();
        return bmp;
    }

It is derivative of answers here: https://stackoverflow.com/a/27077188/8302901

If your control is an instance of the control that is already on the screen, skip the measure, arrange and layout steps and just use the already calculated size.

Dan
  • 858
  • 7
  • 18