• [UWP]用Win2D实现镂空文字


    1. 前言

    之前用PointLight做了一个番茄钟,效果还不错,具体可见这篇文章:

    [UWP]使用PointLight并实现动画效果

    后来试玩了Win2D,这次就用Win2D实现文字的镂空效果,配合PointLight做一个内敛不张扬的番茄钟。

    实现镂空文字的核心思想是使用CanvasGeometry.CreateText从TextLayout获取一个Geometry,然后使用DrawGeometry将它画到DrawingSurface。这篇文章介绍了具体的实现步骤。

    2. 参考例子

    Win2D Gallery提供了大量Win2D的Sample,这次就参考了其中的文字镂空效果例子,地址和运行效果如下:

    https://github.com/microsoft/Win2D-Samples/blob/master/ExampleGallery/TextOutlines.xaml.cs

    3. 实现步骤

    Sample的代码量虽多,其实核心并不复杂,下面讲讲需要用到的API:

    3.1 CanvasDevice.GetSharedDevice

    因为要用到Win2D,所以首先要引用Win2D.uwp nuget包。因为我的目标不是输出到CanvasControl上,而是想要输出到一个SpriteVisual上,所以使用CanvasDevice

    var canvasDevice = CanvasDevice.GetSharedDevice();
    

    3.2 CanvasComposition.CreateCompositionGraphicsDevice

    然后创建一个Compositor,并将这个Compositor和CanvasDevice关联起来,这里需要使用 CanvasComposition 创建 GraphicsDevice

    var compositor = ElementCompositionPreview.GetElementVisual(this).Compositor;
    var graphicsDevice = CanvasComposition.CreateCompositionGraphicsDevice(compositor, canvasDevice);
    

    3.3 CompositionGraphicsDevice.CreateDrawingSurface

    然后使用CompositionGraphicsDevice.CreateDrawingSurface创建一个CompositionDrawingSurface对象,它是用来绘画内容的表面:

    var drawingSurface = graphicsDevice.CreateDrawingSurface(e.NewSize, DirectXPixelFormat.B8G8R8A8UIntNormalized, DirectXAlphaMode.Premultiplied);
    

    3.4 Compositor.CreateSurfaceBrush

    使用Compositor.CreateSurfaceBrush创建一个CompositionSurfaceBrush,它的作用是使用像素绘制SpriteVisual,简单来说它就是一张位图,然后输出到SpriteVisual上:

    var maskSurfaceBrush = compositor.CreateSurfaceBrush(drawingSurface);
    spriteTextVisual.Brush = maskSurfaceBrush;
    

    3.5 CanvasComposition.CreateDrawingSession

    有了CompositionDrawingSurface就可以为所欲为了,将这个DrawingSurface作为参数,调用CanvasComposition.CreateDrawingSession创建DrawingSession,DrawingSession提供了多个函数,可以自由地在DrawingSurface上画文字、形状、图片甚至SVG。

    using (var session = CanvasComposition.CreateDrawingSession(drawingSurface))
    {
    
    }
    

    3.6 CanvasTextFormat和CanvasTextLayout

    要再DrawingSurface上写字,需要CanvasTextLayout,而CanvasTextLayout中的文字大小、格式等则由CanvasTextFormat定义:

    using (var textFormat = new CanvasTextFormat()
    {
        FontSize = (float)FontSize,
        Direction = CanvasTextDirection.LeftToRightThenTopToBottom,
        VerticalAlignment = CanvasVerticalAlignment.Center,
        HorizontalAlignment = CanvasHorizontalAlignment.Center,
    
    })
    {
        using (var textLayout = new CanvasTextLayout(session, Text, textFormat, width, height))
        {
            Color fontColor = FontColor;
            session.DrawTextLayout(textLayout, 0, 0, fontColor);
        }
    }
    

    3.7 CanvasGeometry.CreateText

    因为我的目标是镂空的文字,所以不能直接使用DrawTextLayout。这里需要使用CanvasGeometry.CreateText从TextLayout获取一个Geometry,然后使用DrawGeometry将它画到DrawingSurface。CanvasStrokeStyle是可选的,它控制边框的虚线。

    using (var textGeometry = CanvasGeometry.CreateText(textLayout))
    {
        var dashedStroke = new CanvasStrokeStyle()
        {
            DashStyle = DashStyle
        };
        session.DrawGeometry(textGeometry, OutlineColor, (float)StrokeWidth, dashedStroke);
    }
    

    4. 封装为控件

    将上面的代码总结一下,封装为一个OutlineTextControl 控件,它提供了Text、OutlineColor、FontColor等属性,在控件SizeChanged时,或者各个属性改变时调用DrawText重新在CompositionDrawingSurface上绘制文字。代码大致如下:

    public class OutlineTextControl : Control
    {
        private CompositionDrawingSurface _drawingSurface;
    
        public OutlineTextControl()
        {
            var compositor = ElementCompositionPreview.GetElementVisual(this).Compositor;
            var graphicsDevice = CanvasComposition.CreateCompositionGraphicsDevice(compositor, CanvasDevice.GetSharedDevice());
            var spriteTextVisual = compositor.CreateSpriteVisual();
    
            ElementCompositionPreview.SetElementChildVisual(this, spriteTextVisual);
            SizeChanged += (s, e) =>
            {
                _drawingSurface = graphicsDevice.CreateDrawingSurface(e.NewSize, DirectXPixelFormat.B8G8R8A8UIntNormalized, DirectXAlphaMode.Premultiplied);
                DrawText();
                var maskSurfaceBrush = compositor.CreateSurfaceBrush(_drawingSurface);
                spriteTextVisual.Brush = maskSurfaceBrush;
                spriteTextVisual.Size = e.NewSize.ToVector2();
            };
            RegisterPropertyChangedCallback(FontSizeProperty, new DependencyPropertyChangedCallback((s, e) =>
            {
                DrawText();
            }));
        }
    
    
        private void DrawText()
        {
            if (ActualHeight == 0 || ActualWidth == 0 || string.IsNullOrWhiteSpace(Text) || _drawingSurface == null)
                return;
    
            var width = (float)ActualWidth;
            var height = (float)ActualHeight;
            using (var session = CanvasComposition.CreateDrawingSession(_drawingSurface))
            {
                session.Clear(Colors.Transparent);
                using (var textFormat = new CanvasTextFormat()
                {
                    FontSize = (float)FontSize,
                    Direction = CanvasTextDirection.LeftToRightThenTopToBottom,
                    VerticalAlignment = CanvasVerticalAlignment.Center,
                    HorizontalAlignment = CanvasHorizontalAlignment.Center,
    
                })
                {
                    using (var textLayout = new CanvasTextLayout(session, Text, textFormat, width, height))
                    {
                        if (ShowNonOutlineText)
                        {
                            session.DrawTextLayout(textLayout, 0, 0, FontColor);
                        }
    
                        using (var textGeometry = CanvasGeometry.CreateText(textLayout))
                        {
                            var dashedStroke = new CanvasStrokeStyle()
                            {
                                DashStyle = DashStyle
                            };
                            session.DrawGeometry(textGeometry, OutlineColor, (float)StrokeWidth, dashedStroke);
                        }
                    }
                }
            }
        }
    
    //SOME CODE AND PROPERTIES
    
    }
    

    5. 结语

    文章开头的那个番茄钟源码可以在这里查看:

    OnePomodoro_OutlineTextView.xaml at master

    也可以安装我的番茄钟应用试玩一下,安装地址:

    一个番茄钟

    6. 参考

    CanvasComposition Class

    CanvasDrawingSession Class

    CanvasGeometry Class

    CompositionGraphicsDevice Class (Windows.UI.Composition) - Windows UWP applications _ Microsoft Docs

    CompositionDrawingSurface Class (Windows.UI.Composition) - Windows UWP applications _ Microsoft Docs

    CompositionGraphicsDevice Class (Windows.UI.Composition) - Windows UWP applications _ Microsoft Docs

    CompositionSurfaceBrush Class (Windows.UI.Composition) - Windows UWP applications _ Microsoft Docs

  • 相关阅读:
    第十周阅读内容
    第十周学习小结
    第九周阅读内容
    第九周学习小结
    第八周学习小结
    ..总结
    .总结
    总结.
    周总结
    总结
  • 原文地址:https://www.cnblogs.com/dino623/p/outline_text_with_win2d.html
Copyright © 2020-2023  润新知