microsoft / Win2D

Win2D is an easy-to-use Windows Runtime API for immediate mode 2D graphics rendering with GPU acceleration. It is available to C#, C++ and VB developers writing apps for the Windows Universal Platform (UWP). It utilizes the power of Direct2D, and integrates seamlessly with XAML and CoreWindow.
http://microsoft.github.io/Win2D
Other
1.82k stars 287 forks source link

Blur a round image around the edge? #247

Closed hholhjem closed 8 years ago

hholhjem commented 8 years ago

I have tried to solve this using Win2D but have neither managed to blur a round image or to blur an image only around edge/border. Is this possible with the Win2D framework?

Help appreciated. Thanks!

shawnhar commented 8 years ago

To blur a circle:

        void canvasControl_Draw(CanvasControl sender, CanvasDrawEventArgs args)
        {
            var circle = new CanvasCommandList(sender);

            using (var ds = circle.CreateDrawingSession())
            {
                ds.FillCircle(100, 100, 50, Colors.Red);
            }

            var blurredCircle = new GaussianBlurEffect
            {
                Source = circle,
                BlurAmount = 20
            };

            args.DrawingSession.DrawImage(blurredCircle);
        }

or am I understanding this question wrong?

Win2D doesn't have a built-in effect to blur only the edges of an image, but you could implement this yourself using PixelShaderEffect.

hholhjem commented 8 years ago

Thanks for the tip @shawnhar!

I was a bit unclear with what I am actually trying to achieve. Let me clarify.

I have a square image that i want to crop round (circle) and fade around the edge/border. There is a blurred background behind the image and I am trying create a look, that the image fades together with the blurred background.

Is there an easy way to achieve this with Win2D?

I looked a bit at PixelShaderEffect, but need some more time to get a working example. If there is no easier way I guess I have look some more into this.

13thsymphony commented 8 years ago

I mocked this up in Word, is this what you are trying to do? (Assume the red pattern background has a blur applied).

blurred_mask_blend

Here is one way to accomplish this:

  1. Create a blurred circle using Shawn's example code above. Instead of calling DrawImage, create a CanvasImageBrush from the gaussian blur effect. This will be used as an opacity mask.
  2. Draw the blurred background (DrawImage) to your render target.
  3. Call FillGeometry; use the square image for the brush; use #1 for the opacityBrush.
hholhjem commented 8 years ago

Thanks @13thsymphony! Your mock is correct :)

Here is an exact example made in Word using a random picture online: demo picture

This is what I have for now. It doesn't add up for me yet. Btw: sorry for ugly testcode.

XAML file: <canvas:CanvasControl Draw="CanvasControl_Draw" CreateResources="CanvasControl_CreateResources" ClearColor="White" Margin="0,0,0,0" Width="360" HorizontalAlignment="Center"/>

XAML.cs file: bool loadingRecourcesComplete = false; CanvasImageBrush canvasImageBrushOpacityMask; CanvasBitmap image; CanvasImageBrush imageBrush;

    void CanvasControl_Draw(CanvasControl sender, CanvasDrawEventArgs args)
    {
        if (loadingRecourcesComplete)
        {
            var session = args.DrawingSession;

            // ADD BACKGROUND
            session.Units = CanvasUnits.Pixels;
            double displayScaling = DisplayInformation.GetForCurrentView().LogicalDpi / 96.0;
            double pixelWidth = sender.ActualWidth * displayScaling;
            var scalefactor = pixelWidth / image.Size.Width;
            var scaleEffect = new ScaleEffect();
            scaleEffect.Source = image;
            scaleEffect.Scale = new Vector2()
            {
                X = (float)scalefactor,
                Y = (float)scalefactor
            };

            var blurEffect = new GaussianBlurEffect();
            blurEffect.Source = image;
            blurEffect.BlurAmount = 10;

            session.DrawImage(blurEffect, 0.0f, 0.0f);

            // END ADD BACKGROUND

            session.FillGeometry(CanvasGeometry.CreateEllipse(sender, 240, 240, 160, 140), imageBrush, canvasImageBrushOpacityMask);
        }

    }

    private async void CanvasControl_CreateResources(CanvasControl sender, Microsoft.Graphics.Canvas.UI.CanvasCreateResourcesEventArgs args)
    {
        //LOAD IMAGE
        image = await CanvasBitmap.LoadAsync(sender.Device, new Uri("ms-appx:///Assets/test/card_picture_test2.jpg"));
        imageBrush = new CanvasImageBrush(sender.Device, image)
        {
            //ExtendX = CanvasEdgeBehavior.Clamp,
            //ExtendY = CanvasEdgeBehavior.Wrap,
            SourceRectangle = new Rect(0, 0, image.Bounds.Width, image.Bounds.Height)
        };
        //END LOAD IMAGE

        // CREATE OPACITY MASK
        var circle = new CanvasCommandList(sender);

        using (var ds = circle.CreateDrawingSession())
        {
            ds.FillCircle(0, 0, 50, Colors.Pink);
        }

        var blurredCircle = new GaussianBlurEffect
        {
            Source = circle,
            BlurAmount = 20
        };

        canvasImageBrushOpacityMask = new CanvasImageBrush(sender, blurredCircle)
        {
            //ExtendX = CanvasEdgeBehavior.Clamp,
            //ExtendY = CanvasEdgeBehavior.Wrap,
            SourceRectangle = new Rect(0, 0, image.Bounds.Width, image.Bounds.Height),
            Image = image
        };
        // END CREATE OPACITY MASK

        loadingRecourcesComplete = true;

        sender.Invalidate();
    }

Test image file: card_picture_test2

shawnhar commented 8 years ago

You are putting the same image into both image brushes. You want one brush to contain the image, while the other uses your blurred circle as the opacity mask.

shawnhar commented 8 years ago

I'm confused - did you edit your previous comment after I replied to it? That makes it really hard to follow the conversation!

Please describe what problem you are having with the code shown above.

hholhjem commented 8 years ago

Thanks @shawnhar !

I managed to solve my issue with the code above when I removed the image from the canvasImageBrushOpacityMask as you posted.

However I discovered as you probably know that if debugging (pausing) when creating resources it sometimes does not load correctly. E.g. parts of images missing.

shawnhar commented 8 years ago

Cool, so this problem is resolved now?

I am not aware of any issues with pausing while creating resources. Please open a separate issue if you are able to provide a repro app or repro steps for showing that problem.

hholhjem commented 8 years ago

Yes, this is resolved :) (I am planning to provide a repro app showing the problem within a few days.)