Есть ли способ добиться эффекта размытия Acryli c в WPF? - PullRequest
0 голосов
/ 02 августа 2020

Я пытался совместить c размытие acryli c приложения UWP с моим приложением WPF. Я уже пробовал использовать DWM Blur, но он не такой размытый, как Acryli c Bru sh в UWP, а также пробовал использовать setWindowCompositionAttribute (), который в настоящее время отстает от всего моего окна.

Недавно я видел код на github на Windows .UI.Composition-Win32-Samples

Они добились эффекта размытия Acryli c в WPF с использованием взаимодействия Win2D. но проблема в том, что они фактически создают другое окно и размещают его в исходное окно, поскольку пользовательский элемент управления, который сильно мерцает при изменении размера.

есть ли способ реализовать его непосредственно в окне WPF без использования хоста HWND.


Ссылка на проект Github

WPF Acryli c Эффект размытия с использованием Win2D Interop

Это код, который они используют:

CompositionHostControl.xaml.cs

sealed partial class CompositionHostControl : UserControl, IDisposable
{
    private readonly CanvasDevice _canvasDevice;
    private readonly IGraphicsEffect _acrylicEffect;
    private readonly CompositionHost _compositionHost;
    private readonly Compositor _compositor;
    private readonly ContainerVisual _containerVisual;

    private static double _rectWidth;
    private static double _rectHeight;
    private static bool _isAcrylicVisible = false;
    private static SpriteVisual _acrylicVisual;
    private static DpiScale _currentDpi;
    private static CompositionGraphicsDevice _compositionGraphicsDevice;
    private static CompositionSurfaceBrush _noiseSurfaceBrush; 

    public CompositionHostControl()
    {
        InitializeComponent();            

        // Get graphics device.
        _canvasDevice = CanvasDevice.GetSharedDevice();

        // Create host and attach root container for hosted tree.
        _compositionHost = new CompositionHost();
        CompositionHostElement.Child = _compositionHost;
        _compositionHost.RegisterForDispose(this);

        _compositor = _compositionHost.Compositor;
        _containerVisual = _compositor.CreateContainerVisual();
        
        // Create effect graph.
        _acrylicEffect = CreateAcrylicEffectGraph();
    }

    private void CompositionHostControl_Loaded(object sender, RoutedEventArgs e)
    {
        _currentDpi = WindowsMedia.VisualTreeHelper.GetDpi(this);

        _rectWidth = CompositionHostElement.ActualWidth / 2;
        _rectHeight = CompositionHostElement.ActualHeight / 2;

        // Get graphics device.
        _compositionGraphicsDevice = CanvasComposition.CreateCompositionGraphicsDevice(_compositor, _canvasDevice);

        // Create surface. 
        var noiseDrawingSurface = _compositionGraphicsDevice.CreateDrawingSurface(
            new Windows.Foundation.Size(_rectWidth, _rectHeight),
            DirectXPixelFormat.B8G8R8A8UIntNormalized,
            DirectXAlphaMode.Premultiplied);

        // Draw to surface and create surface brush.
        var noiseFilePath = AppDomain.CurrentDomain.BaseDirectory + "Assets\\NoiseAsset_256X256.png";
        LoadSurface(noiseDrawingSurface, noiseFilePath);
        _noiseSurfaceBrush = _compositor.CreateSurfaceBrush(noiseDrawingSurface);

        // Add composition content to tree.
        _compositionHost.SetChild(_containerVisual);
        AddCompositionContent();
    }

    protected override void OnDpiChanged(DpiScale oldDpi, DpiScale newDpi)
    {
        base.OnDpiChanged(oldDpi, newDpi);
        _currentDpi = newDpi;
        Vector3 newScale = new Vector3((float)newDpi.DpiScaleX, (float)newDpi.DpiScaleY, 1);

        // Adjust each child visual scale and offset.
        foreach (SpriteVisual child in _containerVisual.Children)
        {
            child.Scale = newScale;
            var newOffsetX = child.Offset.X * ((float)newDpi.DpiScaleX / (float)oldDpi.DpiScaleX);
            var newOffsetY = child.Offset.Y * ((float)newDpi.DpiScaleY / (float)oldDpi.DpiScaleY);
            child.Offset = new Vector3(newOffsetX, newOffsetY, 0);
        }
    }

    public void AddCompositionContent()
    {
        var acrylicVisualOffset = new Vector3(
            (float)(_rectWidth * _currentDpi.DpiScaleX) / 2,
            (float)(_rectHeight * _currentDpi.DpiScaleY) / 2,
            0);

        // Create visual and set brush.
        _acrylicVisual = CreateCompositionVisual(acrylicVisualOffset);         
        _acrylicVisual.Brush = CreateAcrylicEffectBrush();
    }
    
    SpriteVisual CreateCompositionVisual(Vector3 offset)
    {
        var visual = _compositor.CreateSpriteVisual();
        visual.Size = new Vector2((float)_rectWidth, (float)_rectHeight);
        visual.Scale = new Vector3((float)_currentDpi.DpiScaleX, (float)_currentDpi.DpiScaleY, 1);
        visual.Offset = offset;

        return visual;
    }

    async void LoadSurface(CompositionDrawingSurface surface, string path)
    {
        // Load from stream.
        var storageFile = await StorageFile.GetFileFromPathAsync(path);
        var stream = await storageFile.OpenAsync(FileAccessMode.Read);
        var bitmap = await CanvasBitmap.LoadAsync(_canvasDevice, stream);

        // Draw to surface.
        using (var ds = CanvasComposition.CreateDrawingSession(surface))
        {
            ds.Clear(Colors.Transparent);

            var rect = new Windows.Foundation.Rect(0, 0, _rectWidth, _rectHeight);
            ds.DrawImage(bitmap, 0, 0, rect);
        }

        stream.Dispose();
        bitmap.Dispose();
    }

    IGraphicsEffect CreateAcrylicEffectGraph()
    {
        return new BlendEffect
        {
            Mode = BlendEffectMode.Overlay,
            Background = new CompositeEffect
            {
                Mode = CanvasComposite.SourceOver,
                Sources =
                        {
                        new BlendEffect
                        {
                            Mode = BlendEffectMode.Exclusion,
                            Background = new SaturationEffect
                            {
                                Saturation = 2,
                                Source = new GaussianBlurEffect
                                {
                                    Source = new CompositionEffectSourceParameter("Backdrop"),
                                    BorderMode = EffectBorderMode.Hard,
                                    BlurAmount = 30
                                },
                            },                                
                            Foreground = new ColorSourceEffect()
                            {
                                Color = Color.FromArgb(26, 255, 255, 255)
                            }
                        },
                        new ColorSourceEffect
                        {
                            Color = Color.FromArgb(153, 255, 255, 255)
                        }
                    }
            },
            Foreground = new OpacityEffect
            {
                Opacity = 0.03f,
                Source = new BorderEffect()
                {
                    ExtendX = CanvasEdgeBehavior.Wrap,
                    ExtendY = CanvasEdgeBehavior.Wrap,
                    Source = new CompositionEffectSourceParameter("Noise")
                },
            },
        };
    }

    CompositionEffectBrush CreateAcrylicEffectBrush()
    {
        // Compile the effect.
        var effectFactory = _compositor.CreateEffectFactory(_acrylicEffect);

        // Create Brush.
        var acrylicEffectBrush = effectFactory.CreateBrush();

        // Set sources.
        var destinationBrush = _compositor.CreateBackdropBrush();
        acrylicEffectBrush.SetSourceParameter("Backdrop", destinationBrush);
        acrylicEffectBrush.SetSourceParameter("Noise", _noiseSurfaceBrush);

        return acrylicEffectBrush;
    }

    public void Dispose()
    {
        _acrylicVisual.Dispose();
        _noiseSurfaceBrush.Dispose();

        _canvasDevice.Dispose();
        _compositionGraphicsDevice.Dispose();
}

    internal void ToggleAcrylic()
    {
        // Toggle visibility of acrylic visual by adding or removing from tree.
        if (_isAcrylicVisible)
        {
            _containerVisual.Children.Remove(_acrylicVisual);
        }
        else
        {
            _containerVisual.Children.InsertAtTop(_acrylicVisual);
        }

        _isAcrylicVisible = !_isAcrylicVisible;
    }
}

CompositionHost.cs

sealed class CompositionHost : HwndHost
{
    private readonly object _dispatcherQueue;
    private readonly List<IDisposable> _registeredDisposables = new List<IDisposable>();
    private readonly ICompositorDesktopInterop _compositorDesktopInterop;

    private ICompositionTarget _compositionTarget;

    public IntPtr HwndHost { get; private set; }
    public Compositor Compositor { get; private set; }

    public CompositionHost()
    {
        // Create dispatcher queue.
        _dispatcherQueue = InitializeCoreDispatcher();

        Compositor = new Compositor();
        _compositorDesktopInterop = (ICompositorDesktopInterop)(object)Compositor;
    }

    public void SetChild(Visual v)
    {
        _compositionTarget.Root = v;
    }

    protected override HandleRef BuildWindowCore(HandleRef hwndParent)
    {
        // Create Window
        HwndHost = IntPtr.Zero;
        HwndHost = User32.CreateWindowExW(
                               dwExStyle: User32.WS_EX.WS_EX_TRANSPARENT,
                               lpClassName: "Message",
                               lpWindowName: "CompositionHost",
                               dwStyle: User32.WS.WS_CHILD,
                               x: 0, y: 0,
                               nWidth: 0, nHeight: 0,
                               hWndParent: hwndParent.Handle,
                               hMenu: IntPtr.Zero,
                               hInstance: IntPtr.Zero,
                               lpParam: IntPtr.Zero);

        // Get compositor and target for hwnd.
        _compositorDesktopInterop.CreateDesktopWindowTarget(HwndHost, true, out _compositionTarget);

        return new HandleRef(this, HwndHost);
    }

    protected override void DestroyWindowCore(HandleRef hwnd)
    {
        if (_compositionTarget.Root != null)
        {
            _compositionTarget.Root.Dispose();
        }

        User32.DestroyWindow(hwnd.Handle);

        foreach (var d in _registeredDisposables)
        {
            d.Dispose();
        }
    }

    // Register a given IDisposable object for disposal during cleanup.
    public void RegisterForDispose(IDisposable d)
    {
        _registeredDisposables.Add(d);
    }

    private object InitializeCoreDispatcher()
    {
        DispatcherQueueOptions options = new DispatcherQueueOptions
        {
            apartmentType = DISPATCHERQUEUE_THREAD_APARTMENTTYPE.DQTAT_COM_STA,
            threadType = DISPATCHERQUEUE_THREAD_TYPE.DQTYPE_THREAD_CURRENT,
            dwSize = Marshal.SizeOf(typeof(DispatcherQueueOptions))
        };

        var hresult = CreateDispatcherQueueController(options, out object queue);
        if (hresult != 0)
        {
            Marshal.ThrowExceptionForHR(hresult);
        }

        return queue;
    }

    internal enum DISPATCHERQUEUE_THREAD_APARTMENTTYPE
    {
        DQTAT_COM_NONE = 0,
        DQTAT_COM_ASTA = 1,
        DQTAT_COM_STA = 2
    };

    internal enum DISPATCHERQUEUE_THREAD_TYPE
    {
        DQTYPE_THREAD_DEDICATED = 1,
        DQTYPE_THREAD_CURRENT = 2,
    };

    [StructLayout(LayoutKind.Sequential)]
    internal struct DispatcherQueueOptions
    {
        public int dwSize;

        [MarshalAs(UnmanagedType.I4)]
        public DISPATCHERQUEUE_THREAD_TYPE threadType;

        [MarshalAs(UnmanagedType.I4)]
        public DISPATCHERQUEUE_THREAD_APARTMENTTYPE apartmentType;
    };

    [DllImport("coremessaging.dll")]
    internal static extern int CreateDispatcherQueueController(DispatcherQueueOptions options,
                                            [MarshalAs(UnmanagedType.IUnknown)]
                                           out object dispatcherQueueController);
}

[ComImport]
[Guid("29E691FA-4567-4DCA-B319-D0F207EB6807")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface ICompositorDesktopInterop
{
    void CreateDesktopWindowTarget(IntPtr hwndTarget, bool isTopmost, out ICompositionTarget target);
}

[ComImport]
[Guid("A1BEA8BA-D726-4663-8129-6B5E7927FFA6")]
[InterfaceType(ComInterfaceType.InterfaceIsIInspectable)]
public interface ICompositionTarget
{
    Visual Root
    {
        get;
        set;
    }
}
...