• 【Win 10 应用开发】UI Composition 札记(四):绘制图形


    使用 Win 2D 组件,就可以很轻松地绘制各种图形,哪怕你没有 D2D 相关基础,也不必写很复杂的 C++ 代码。

    先来说说如何获取 Win 2D 组件。很简单,创建 UWP 应用项目后,你打开“解决方案资源管理器”窗口,然后在【引用】节点上右击,从快捷菜单中选择【管理 Nuget 程序包】命令,在打开的窗口中搜索“Win 2D”,然后安装带有 uwp 标识的那个就可以了。

    顺便说一下,nuget 的包缓存在你的用户文件夹下,就是系统盘下的 usersxxx,xxx是你登录系统的用户名,在文件夹下有个 .nuget 目录,packages 子目录下就是缓存的包,大小取决你安装的组件,大的时候 4、5 个G也有的。

    在你的应用项目中,VS 只是创建了一个 JSON 文件来描述你引用的组件,Win 2D 添加引用成功后,你的引用列表应该是这样的。

    你如果看到 Win2D.uwp 这个项目,那就没问题了。

    不过,你得注意,直接双击它是无法在“对象浏览器”中查看的,你可以这样:打开“对象浏览器”窗口,然后把浏览的子集改为“我的解决方案”,这样,你就能看到你当前项目中所有引用的组件的类型结构了。

     现在,你就能看到 Win2D 库的基本内容了。

    Microsoft.Graphics.Canvas 以及它的子命名空间都是 Win2D 组件中的类型。

    其实,使用 Win2D 组件,你完全可以很简单地绘制各种玩意儿,因为在 Microsoft.Graphics.Canvas.UI.Xaml 命名空间下,直接就提供了一些控件,你可以直接用到 XAML 文档中,然后要画什么就用代码画就行了。比如,我介绍两个比较典型的。

    * CanvasControl —— 可以绘制各种你想要的东东,它就相当于一块画布,用代码绘制时须处理 Draw 事件,然后就在事件处理代码中随便 draw。

    * CanvasAnimatedControl —— 跟上面的那个家伙差不多,只是它可以在你绘制的内容上产生动画。

    大伙会看到,这两个控件都有一个 CreateResources 事件,用来干什么鸟的呢?它的作用是这样的,你可以在处理这个事件的代码中实例化一些资源,这些资源一般在绘制过程不会频繁改动的,比如加载的某个图片,某个画刷等。

    下面给大伙简单演示一下 CanvasControl 控件的用法。

    在 XAML 文档中先要引入命名空间。

    <Page
        ……
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" …… xmlns:canvas="using:Microsoft.Graphics.Canvas.UI.Xaml"> </Page>

    然后就可以用了。

        <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
            <canvas:CanvasControl Draw="OnDraw"/>
        </Grid>

    接着处理 Draw 事件,我们画上几笔试试手。要画东东,我们要用到 Microsoft.Graphics.Canvas 命名空间下的另一个类——CanvasDrawingSession,它公开了许多 Draw 和 Fill 方法,Draw 是画出某个东东,Fill 是填充一个区域。

            private void OnDraw(Microsoft.Graphics.Canvas.UI.Xaml.CanvasControl sender, Microsoft.Graphics.Canvas.UI.Xaml.CanvasDrawEventArgs args)
            {
                using (CanvasDrawingSession dss = args.DrawingSession)
                {
                    // 画线
                    dss.DrawLine(12f, 15f, 335f, 408f, Colors.DarkBlue, 5.5f);
                    // 画矩形
                    dss.DrawRectangle(55f, 190f, 465f, 369f, Colors.Gold, 8f);
                    // 画圆
                    dss.DrawEllipse(350f, 350f, 200f, 260f, Colors.Orange, 5f);
                }
            }

    随便画几下,纯属鬼画符。效果如下图所示。

    但是,我们今天的主题是跟 UI Composition 有关的,虽然上述绘图法很超逸,却不是咱们今天的主题。我们下面要做的,是使用 Composition API 来呈现自己绘制的内容。

    大伙伴们可以思考一下,要在 Composition 组装的对象上画东西,需要什么?前面咱们说过,你得有个 Brush,在 Composition API 中,只有一个画刷可以呈现自己绘制的内容,那就是 CompositionSurfaceBrush。

    要创建 CompositionSurfaceBrush 实例容易,调用一下 Compositor.CreateSurfaceBrush 方法就行了。但问题的核心不在此,而在于,CompositionSurfaceBrush 画刷创建后是空的,要能够呈现内容,还得需要给 Surface 属性赋个值,它是一个实现了 ICompositionSurface 接口的类型,在 Composition API 中,实现了该接口的只有一个类:CompositionDrawingSurface。然而,你看到了,这个类是没有构造函数公开的,它是由 CompositionGraphicsDevice 类的 CreateDrawingSurface 方法创建的。

    好,现在我们可以理一下思路了。

    1、你必须想方设法得到一个 CompositionGraphicsDevice 实例,可该类没公开的构造函数,咋办?所以才要使用 Win2D,稍后再说,Win2D 会有办法获得这个实例的。

    2、调用 CompositionGraphicsDevice 实例的 CreateDrawingSurface 或 CreateDrawingSurface2 方法创建 CompositionDrawingSurface

    实例。

    3、在新创建的 CompositionDrawingSurface 实例上画东西。这个也要用到 Win2D。

    4、使用 Compositor 的静态方法直接创建 CompositionSurfaceBrush 对象,并与上面画好的 CompositionDrawingSurface 关联。

    5、把这个画刷(CompositionSurfaceBrush)与可视化元素关联,比如,SpriteVisual类就有一个 Brush 属性。

    如此一来,我们找到了两个难点:a、如何创建 CompositionGraphicsDevice ; b、如何在 Surface 上画东西(此 Surface 非彼 Surface)。

    借助 Win2D,可以解决以上两个难题。我们不讲理论的,下面用实例来说明。

    首先,在 XAML 文档中随便声明一个元素,只要是 UIElement 的子类就行。

        <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
            <Canvas Name="myCvs"/>
        </Grid>

    记得分配一个名字,待会在代码中要访问。

    随后,我们就可以开始作画了。

            void DrawSomething()
            {
                // 获取 XAML 元素上的 Visual
                Visual canvasVisual = ElementCompositionPreview.GetElementVisual(myCvs);
                // 获取 Compositor 对象
                Compositor compos = canvasVisual.Compositor;
                // 创建 GraphicsDevice 
                CompositionGraphicsDevice graphicsDevice = CanvasComposition.CreateCompositionGraphicsDevice(compos, CanvasDevice.GetSharedDevice());
                // 创建 Surface
                // 画布的大小
                SizeInt32 cvsize = new SizeInt32
                {
                    Width = 600,
                    Height = 400
                };
                CompositionDrawingSurface surface = graphicsDevice.CreateDrawingSurface2(cvsize, Windows.Graphics.DirectX.DirectXPixelFormat.B8G8R8A8UIntNormalized, Windows.Graphics.DirectX.DirectXAlphaMode.Premultiplied);
                // 开始绘画
                using (CanvasDrawingSession dss = CanvasComposition.CreateDrawingSession(surface))
                {
                    // 刷墙,把墙面刷成黑色
                    dss.Clear(Colors.Black);
                    // 画一个圆
                    dss.DrawEllipse(210f, 190f, 80f, 80f, Colors.Yellow, 4f);
                    // 再画一个椭圆
                    dss.DrawEllipse(230f, 200f, 160f, 95f, Colors.SkyBlue, 3.5f);
                    // 填充一块区域
                    dss.FillRectangle(400f, 250f, 150f, 100f, Colors.Pink);
                }
                // 创建 surface 画刷
                CompositionSurfaceBrush sfbrush = compos.CreateSurfaceBrush(surface);
                // 创建一个支持画刷的可视化对象
                SpriteVisual newVisual = compos.CreateSpriteVisual();
                // 设置画刷
                newVisual.Brush = sfbrush;
                // 注意要设置可视化对象的大小
                ExpressionAnimation anim = compos.CreateExpressionAnimation();
                anim.Expression = "parn.Size";
                anim.SetReferenceParameter("parn", canvasVisual);
                newVisual.StartAnimation("Size", anim);
                // 最后别忘了把新的可视化对象插入对象树
                ElementCompositionPreview.SetElementChildVisual(myCvs, newVisual);
            }

    我们上篇中讲过的,与 XAML 交互,使用 ElementCompositionPreview辅助类可以获得与 XAML 元素对应的 Visual 实例,这样我们也能得到相关的 Compositor 对象了。

    注意创建 CompositionGraphicsDevice 实例要借助 Win2D 的 CanvasComposition 类(位于 Microsoft.Graphics.Canvas.UI.Composition 命名空间),在调用 CreateCompositionGraphicsDevice 方法时,你除了得提供关联的 Compositor 对象外,还得有一个兼容的 CanvasDevice 对象。这个 CanvasDevice 对象有个很TMD简单的获取方法,就是直接调用它的 GetSharedDevice 静态方法。

    好了,有了 CompositionGraphicsDevice 实例,那创建 CompositionDrawingSurface对象就好办了,就像这样。

     CompositionDrawingSurface surface = graphicsDevice.CreateDrawingSurface2(cvsize, Windows.Graphics.DirectX.DirectXPixelFormat.B8G8R8A8UIntNormalized, Windows.Graphics.DirectX.DirectXAlphaMode.Premultiplied);

    CreateDrawingSurface 和 CreateDrawingSurface2 都可以,带“2”的是使用整数来表示像素值。

    调用这个方法最麻烦的后面两个参数,它们都是枚举值,如果值设置不当会发生异常,所以,如果出错了,你就得调整了。一般来说,在屏幕上显示的东东,我们会选择 BGRA 的顺序,因为这个顺序呈现出来不会偏色,尤其是图像,如果用 RGBA 就会偏色。B8G8R8A8 表示都用8位的值,也就是一个字节,这个我们平时处理一般图形也够用了(即32位颜色)。

    接下来就是用 CanvasDrawingSession 来画你想画的各种玩意儿,画完后要创建一个 CompositionSurfaceBrush 对象,并且要与刚刚画好的 surface 对象关联。

    最后用这个画刷填充一个支持画刷的可视化对象即可,如 SpriteVisual。记住,一定要设置对象的 Size 属性,因为默认值是0,不然它不会显示出来的,这里呢,我直接用一个表达式动画,让它的大小跟随 Canvas 的大小。

    这个方法封装好后,可以在适当的地方调用,以绘制内容,比如页面类的构造函数中。

            public MainPage()
            {
                this.InitializeComponent();
                DrawSomething();
            }

    好了,看看效果吧。

     OK,今天的内容就说到这里了。

  • 相关阅读:
    Servlet的生命周期
    Servlet中的请求与响应
    Servlet中的相关的配置文件信息:
    转发与重定向的区别,笔记无法转过来,所以直接放链接了,可以直接查看
    JSP入门中的一些案例代码:
    一些已经有些模糊的小知识(一)
    JSP入门五之request,out,response对象的应用
    JSP入门四
    JSP入门三 不知道如何将笔记同步过来只能这样了
    来自(乐唯科技)的面试问题..
  • 原文地址:https://www.cnblogs.com/tcjiaan/p/7808377.html
Copyright © 2020-2023  润新知