Two Image Layers and OpacityMask

2.7k views Asked by At

I'm trying to crop a circle from one image, and put it on top another image in WPF.

The Circle's center changes according to the mouse movements, and needs to be bounded dynamically.

I tried to position two images on top of each other, and use a third image that I draw in real time as an opacity mask.

Could you please provide short code to solve this problem efficiently ?

Illustration

1

There are 1 answers

0
Rob Perkins On BEST ANSWER

The code below describes what you can do with an OpacityMask. It's a little counterintuitive, because we expect a XAML rendering to layer elements bottom-to-top.

However, in this case you want your "background" image to layer on top of the foreground, because the OpacityMask will serve to display only that portion of the foreground described by the position and size of the VisualBrush, rendering the rest transparent. It's given as follows:

<Grid x:Name="MainGrid" MouseMove="Grid_MouseMove">
    <Rectangle Fill="Red" ></Rectangle>
    <Rectangle Fill="Green">
        <Rectangle.OpacityMask>
            <VisualBrush Stretch="None" >
                <VisualBrush.Visual>
                    <Ellipse Width="40" Height="40" StrokeThickness="1" Fill="Black"  />
                </VisualBrush.Visual>
                <VisualBrush.RelativeTransform>
                    <TransformGroup>
                        <TranslateTransform x:Name="OpacityFilterTransform" X="1" Y="1"/>
                    </TransformGroup>
                </VisualBrush.RelativeTransform>
            </VisualBrush>
        </Rectangle.OpacityMask>
    </Rectangle>
</Grid>

Then, this event handler code computes the position of the ellipse and applies it to the OpacityFilter's TranslateTransform object, giving you control over the position of the image.

    private void Grid_MouseMove(object sender, MouseEventArgs e)
    {
        var position = e.GetPosition(this);
        var height = MainGrid.ActualHeight;
        var width = MainGrid.ActualWidth;
        // with the position values, interpolate a TranslateTransform for the opacity mask
        var transX = position.X / width;
        var transY = position.Y / height;
        OpacityFilterTransform.X = transX - 0.5;
        OpacityFilterTransform.Y = transY - 0.5;
    }

This solution should work for any descendant of Visual you care to layer.