dotnet WinUI3 Win2D 翻转图片

news/2024/10/6 2:05:56/文章来源:https://www.cnblogs.com/lindexi/p/18288633

本文将告诉大家如何在 WinUI3 里面使用 Win2D 进行图片的翻转,本文的方法也适用于 UWP 框架

图片的翻转在 Win2D 里面,可以使用 Transform2DEffect 特效来辅助实现,核心逻辑就是通过缩放矩阵当成2D翻转矩阵,将缩放的 X 和 Y 传入负数即可分别实现对应方向的翻转。比如左右水平翻转可将 X 值传入负数,如 -1 表示直接水平翻转

本文接下来将告诉大家一步步进行实现从文件加载图片,再将图片进行翻转在界面显示

在 WinUI3 或 UWP 里面使用 Win2D 需按照 dotnet 的惯例安装 NuGet 库。在 UWP 里面需要安装 Win2D.uwp 库,在 WinUI 3 项目里面需要安装 Microsoft.Graphics.Win2D 库

对于 WinUI 3 项目,由于使用了 SDK 的 csproj 项目文件代码风格,可以编辑 csproj 项目文件,在 ItemGroup 里面添加如下代码进行快速安装库,代码如下

    <PackageReference Include="Microsoft.Graphics.Win2D" Version="1.2.0" />

编辑之后的 csproj 代码文件大概如下

<Project Sdk="Microsoft.NET.Sdk"><PropertyGroup><OutputType>WinExe</OutputType><TargetFramework>net8.0-windows10.0.19041.0</TargetFramework><TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion><RootNamespace>ChaigelyojeeBifakeljair</RootNamespace><ApplicationManifest>app.manifest</ApplicationManifest><Platforms>x86;x64;ARM64</Platforms><RuntimeIdentifiers Condition="$([MSBuild]::GetTargetFrameworkVersion('$(TargetFramework)')) &gt;= 8">win-x86;win-x64;win-arm64</RuntimeIdentifiers><RuntimeIdentifiers Condition="$([MSBuild]::GetTargetFrameworkVersion('$(TargetFramework)')) &lt; 8">win10-x86;win10-x64;win10-arm64</RuntimeIdentifiers><PublishProfile>win-$(Platform).pubxml</PublishProfile><UseWinUI>true</UseWinUI><EnableMsixTooling>true</EnableMsixTooling></PropertyGroup><ItemGroup><Content Include="Assets\SplashScreen.scale-200.png" /><Content Include="Assets\LockScreenLogo.scale-200.png" /><Content Include="Assets\Square150x150Logo.scale-200.png" /><Content Include="Assets\Square44x44Logo.scale-200.png" /><Content Include="Assets\Square44x44Logo.targetsize-24_altform-unplated.png" /><Content Include="Assets\StoreLogo.png" /><Content Include="Assets\Wide310x150Logo.scale-200.png" /></ItemGroup><ItemGroup><PackageReference Include="Microsoft.Graphics.Win2D" Version="1.2.0" /><PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.756" /><PackageReference Include="Microsoft.WindowsAppSDK" Version="1.5.240607001" /><Manifest Include="$(ApplicationManifest)" /></ItemGroup><!--Defining the "Msix" ProjectCapability here allows the Single-project MSIX PackagingTools extension to be activated for this project even if the Windows App SDK Nugetpackage has not yet been restored.--><ItemGroup Condition="'$(DisableMsixProjectCapabilityAddedByProject)'!='true' and '$(EnableMsixTooling)'=='true'"><ProjectCapability Include="Msix" /></ItemGroup><!--Defining the "HasPackageAndPublishMenuAddedByProject" property here allows the SolutionExplorer "Package and Publish" context menu entry to be enabled for this project even ifthe Windows App SDK Nuget package has not yet been restored.--><PropertyGroup Condition="'$(DisableHasPackageAndPublishMenuAddedByProject)'!='true' and '$(EnableMsixTooling)'=='true'"><HasPackageAndPublishMenu>true</HasPackageAndPublishMenu></PropertyGroup>
</Project>

本文的示例里面,将编写在 MainWindow.xaml 里面,先添加命名空间 xmlns:canvas="using:Microsoft.Graphics.Canvas.UI.Xaml" 的引用

接着在 xaml 里面添加上 CanvasControl 控件,同时监听 CreateResources 和 Draw 事件,代码如下

 <canvas:CanvasControl x:Name="Canvas" ClearColor="Black" CreateResources="Canvas_OnCreateResources" Draw="Canvas_OnDraw"/>

按照 Win2D 的设计,咱将在 CreateResources 事件里面,进行本地文件的加载作为图片,在 Draw 事件里面进行绘制

为了演示图片翻转,咱需要先有图片。本文这里将读取本地文件作为图片。实现的逻辑放在 Canvas_OnCreateResources 方法里面

代码如下

    private void Canvas_OnCreateResources(CanvasControl sender, CanvasCreateResourcesEventArgs args){var imageFile = @"C:\lindexi\Image\1.png";// 图片地址大家自己替换if (!File.Exists(imageFile)){// 自己换成自己的图片Debugger.Break();}var task = LoadImageAsync();args.TrackAsyncAction(task.AsAsyncAction());async Task LoadImageAsync(){CanvasBitmap canvasBitmap = await CanvasBitmap.LoadAsync(sender, imageFile);_canvasBitmap = canvasBitmap;}}private CanvasBitmap? _canvasBitmap;

请大家将上面代码的 C:\lindexi\Image\1.png 路径替换为你自己的本地图片文件的路径

以上代码写了一个名为 LoadImageAsync 的内部方法,这是因为加载图片需要用到异步,需要包装 Task 作为异步任务,再将异步任务通过 TrackAsyncAction 告知给到 Win2D 层。如此即可让 Win2D 等待 LoadImageAsync 完成才完成资源创建逻辑,接着再执行 Draw 绘制。如果没有使用 TrackAsyncAction 方式告知 Win2D 的话,那可能在资源加载完成之前,就会进入到 Draw 绘制导致状态不符合预期

换句话说,直接将 Canvas_OnCreateResources 改为 async void 是不可以的,一旦这么做了,那 Win2D 层是无法感知到资源异步加载完成的,也就让 Win2D 层无法知道在何时才是合适的触发渲染

完成图片资源加载逻辑之后,接下来进入到核心的 Canvas_OnDraw 方法

图片的翻转在 Win2D 里面,可以使用 Transform2DEffect 特效来辅助实现,核心逻辑就是通过缩放矩阵当成2D翻转矩阵,将缩放的 X 和 Y 传入负数即可分别实现对应方向的翻转。本文以下将演示如何将图片进行水平翻转。还请大家不用担心用到矩阵,本文这里不会直接用到多少矩阵知识,只是简单调用方法而已

先从字段 _canvasBitmap 获取 CanvasBitmap 类型的对象,保持稳定的话需要判断一次空,防止资源创建步骤出现诡异的事情导致没有创建成功

    private void Canvas_OnDraw(CanvasControl sender, CanvasDrawEventArgs args){if (_canvasBitmap is { } canvasBitmap){... // 忽略其他代码}}private CanvasBitmap? _canvasBitmap;

以上代码只写了 if 为 true 的代码,在实际产品代码里面推荐也加上 else 打上日志或进行其他处理

对图片进行中心点水平翻转,可以使用 Matrix3x2 创建缩放矩阵,在 X 方向传入 -1 表示翻转,在 Y 方向传入 1 表示不变,再传入图片的中心点即可

先获取图片的中心点,代码如下

    private void Canvas_OnDraw(CanvasControl sender, CanvasDrawEventArgs args){if (_canvasBitmap is { } canvasBitmap){var centerX = canvasBitmap.Bounds._width / 2;var centerY = canvasBitmap.Bounds._height / 2;... // 忽略其他代码}}

这里的中心点直接取宽度高度的一半是因为图片本身没有平移,且接下来的特效是基于当前图片的坐标系。相当于图片的左上角就是 0 0 点,直接取宽度高度一半就是刚好中心点的值

接下来按照 win10 uwp win2d 入门 看这一篇就够了 和 win10 uwp win2d 特效 里面提供的方法,创建 Transform2DEffect 特效,代码如下

            var transform2DEffect = new Transform2DEffect();

将当前的图片作为特效的输入源,代码如下

            transform2DEffect.Source = canvasBitmap;

接下来对 Transform2DEffect 传入翻转矩阵,即在 X 方向传入 -1 表示翻转,在 Y 方向传入 1 表示不变,再传入图片的中心点,代码如下

            var matrix3X2 = Matrix3x2.CreateScale(-1, 1, new Vector2(centerX, centerY));transform2DEffect.TransformMatrix = matrix3X2;

可以看到本文只是简单调用 Matrix3x2 的 CreateScale 方法,没有使用到多少的矩阵知识

完成特效之后,即可将 Transform2DEffect 传入到 CanvasDrawEventArgs 进行绘制,代码如下

            args.DrawingSession.DrawImage(transform2DEffect);

如此即可完成图片的左右翻转

有伙伴可能好奇,使用本文的特效方式对图片进行翻转,性能如何呢?答案是性能是特别高的,在 Win2D 里面绝大部分特效对于 GPU 来说时间复杂度都是 O(1) 级,这是什么概念呢,用简单的话说就是 GPU 一口气就能做完,不耗资源的

以上就是本文提供的简单示例代码,在 WinUI 3 或 UWP 里面使用 Win2D 进行翻转图片。本文以上的例子代码只是做水平左右翻转,相信阅读了以上内容的伙伴自己也能实现垂直翻转功能

再近一步,如果图片比较大或比较小,我想要再次缩放图片,让图片刚好在界面里面填充显示,可以如何实现?简单实现那还是使用 Transform2DEffect 特效,在 Win2D 里面可以特效套特效,此时的渲染效率依然是特别高的。在上文,咱使用缩放矩阵当成翻转矩阵,那接下来对图片的缩放就可以将缩放矩阵当成缩放矩阵了

将以上的 transform2DEffect 作为下一个 Transform2DEffect 的输入源,在新的 Transform2DEffect 对图片进行缩放,代码如下

            var transform2DEffect2 = new Transform2DEffect(){Source = transform2DEffect,TransformMatrix = Matrix3x2.CreateScale((float) (sender.ActualWidth / canvasBitmap.Bounds.Width), (float) (sender.ActualHeight / canvasBitmap.Bounds.Height))};

最后再将 transform2DEffect2 输出到界面,代码如下

            args.DrawingSession.DrawImage(transform2DEffect2);

以上代码的 transform2DEffect2 就是使用当前的 CanvasControl 的实际布局尺寸和图片的尺寸进行比例缩放,如此即可将图片拉伸填充到相同的尺寸。这一点是非常简单的,如果大家想不明白的话,试试拿出纸张和笔画一画,基础初中知识就可以理解

以上代码更多是和大家演示在 Win2D 里面将两个特效进行叠加的写法。在实际应用里面,可以将上述两个 Transform2DEffect 进行合并,因为这两个 Transform2DEffect 都只是进行 TransformMatrix 叠加而已,可以进行更简单的数学计算。接下来部分需要用到点矩阵的知识,即上述的两个特效的叠加和对两个 TransformMatrix 矩阵进行相乘的结果是相同的。此时可以只用一个 Transform2DEffect 然后将本文的两个 Transform2DEffect 的 TransformMatrix 进行相乘即可,如此可以省略一次特效

尽管特效对于 GPU 来说大多都是不费资源的,对 Transform2DEffect 来说,资源使用量就更少了。但是能够通过基础 CPU 进行简单数学计算,还是能做非常多的性能提升的。这里需要额外说明的是性能是相对的,尽管说通过基础 CPU 进行简单数学计算能做非常多的性能提升,但是用人话说就是从一分钱提升到零点一分钱而已,约等于依然还是不用钱

为了更好的进行演示效果,我这里还在界面添加了一个 ToggleButton 用于点击的时候控制是否翻转,界面代码如下

<ToggleButton HorizontalAlignment="Center" VerticalAlignment="Center" Content="是否翻转" Click="Button_OnClick"/>

修改之后的界面代码如下

<?xml version="1.0" encoding="utf-8"?>
<Windowx:Class="ChaigelyojeeBifakeljair.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:local="using:ChaigelyojeeBifakeljair"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"xmlns:canvas="using:Microsoft.Graphics.Canvas.UI.Xaml"mc:Ignorable="d" Closed="MainWindow_OnClosed"><Grid><canvas:CanvasControl x:Name="Canvas" ClearColor="Black" CreateResources="Canvas_OnCreateResources" Draw="Canvas_OnDraw"/><ToggleButton HorizontalAlignment="Center" VerticalAlignment="Center" Content="是否翻转" Click="Button_OnClick"/></Grid>
</Window>

在点击的时候设置一个字段且让 Win2D 画布进行重新绘制,代码如下

    private bool _shouldFlip = false;private void Button_OnClick(object sender, RoutedEventArgs e){_shouldFlip = (sender as ToggleButton)?.IsChecked is true;Canvas.Invalidate();}

修改一下 Canvas_OnDraw 的代码,判断 _shouldFlip 字段决定翻转,修改之后的代码如下

    private void Canvas_OnDraw(CanvasControl sender, CanvasDrawEventArgs args){if (_canvasBitmap is { } canvasBitmap){var centerX = canvasBitmap.Bounds._width / 2;var centerY = canvasBitmap.Bounds._height / 2;var transform2DEffect = new Transform2DEffect();transform2DEffect.Source = canvasBitmap;var flip = _shouldFlip ? -1 : 1;var matrix3X2 = Matrix3x2.CreateScale(flip, 1, new Vector2(centerX, centerY));transform2DEffect.TransformMatrix = matrix3X2;var transform2DEffect2 = new Transform2DEffect(){Source = transform2DEffect,TransformMatrix = Matrix3x2.CreateScale((float) (sender.ActualWidth / canvasBitmap.Bounds.Width), (float) (sender.ActualHeight / canvasBitmap.Bounds.Height))};args.DrawingSession.DrawImage(transform2DEffect2);}}

也许有伙伴说如果没有翻转了,那不如将 transform2DEffect 去掉好了。没错,但这个特效基本不耗资源,那就跑着咯

最后别忘了在窗口关闭的时候,清理 Win2D 的资源

    private void MainWindow_OnClosed(object sender, WindowEventArgs args){Canvas.RemoveFromVisualTree();}

本文代码放在 github 和 gitee 上,可以使用如下命令行拉取代码

先创建一个空文件夹,接着使用命令行 cd 命令进入此空文件夹,在命令行里面输入以下代码,即可获取到本文的代码

git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin bb784a22d576278e2f6dfb878e8c760128e91dad

以上使用的是 gitee 的源,如果 gitee 不能访问,请替换为 github 的源。请在命令行继续输入以下代码,将 gitee 源换成 github 源进行拉取代码

git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git
git pull origin bb784a22d576278e2f6dfb878e8c760128e91dad

获取代码之后,进入 DirectX/Win2D/ChaigelyojeeBifakeljair 文件夹,即可获取到源代码

更多 UWP 或 WinUI3 开发教程,以及更多渲染相关教程,请参阅 博客导航

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/739821.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

UWP WinUI 制作一个路径矢量图标按钮样式入门

本文将告诉大家如何在 UWP 或 WinUI3 或 UNO 里,如何制作一个路径按钮。路径按钮就是使用几何路径轮廓表示内容的按钮,常见于各种图标按钮,或 svg 系贴图矢量图按钮在网上有非常多矢量图库,其中免费的图库也非常多,比如 https://www.iconfont.cn/ 等等。在咱的应用程序里面…

【攻防技术系列+代理转发】工具--netcat

【需求】现在想要实现两个不同网段的私网之间相互通信,我们该如何做呢?🔴实验环境:【kali(攻击端)】:192.168.10.131 【centos7(跳板机)】:192.168.10.39;172.16.80.130 【win7】:172.16.80.131 工具:netcat【kali】: 开启监听【centos7】:【kali】: 获得对方的…

基于FPGA的A律压缩解压缩verilog实现,包含testbench

1.算法仿真效果 VIVADO2019.2仿真结果如下(完整代码运行后无水印):RTL图如下所示:2.算法涉及理论知识概要A律压缩是一种广泛应用于语音编码的非均匀量化技术,尤其在G.711标准中被欧洲和中国等国家采纳。该技术的核心目的是在有限的带宽下高效传输语音信号,同时保持较高的…

LFU算法实现

LFU (Least Frequently Used) 是一种用于缓存管理的算法。它通过跟踪每个缓存项被访问的频率来决定哪些项应该被移除。LFU算法倾向于保留那些使用频率较高的项,而移除那些使用频率较低的项。以下是LFU算法的详细介绍: 工作原理计数器:每个缓存项都有一个计数器,用于记录该项…

灰色预测GM(1,1)模型的理论原理

灰色预测是对时间有关的灰色过程进行预测。通过建立相应的微分方程模型,从而预测事物未来发展趋势的状况。 由于笔者的水平不足,本章只是概括性地介绍GM(1,1)模型的理论原理,便于对初学者的初步理解 目录一、灰色系统二、GM(1,1)灰色预测模型1.生成累加数据与紧临均值生成…

JMonkeyEngine——材质文件备注

默认J3M编辑器不支持编辑纹理参数的Mag/Min滤波选项,只能配置Flip和Wrap模式,但是可以单独编辑J3M源码,如下: 添加你需要的Mag/Min滤波选项,参考源码的解析,就是Mag/Min+拼接对应的Filter值。 虽然打开J3M编辑器会报错: 但实际进游戏时并不会报错,而且一切正常,如下:…

04-JS中的面向对象ES5

JS面向对象基础01 JS对象中key的类型02 创建对象的方法03 对象的常见操作 3.1 访问对象的属性 <!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="I…

程序员的AI工作流

AI 工具在日常工作中的应用逐渐成为程序员必备利器。本文介绍了作者常用的一些 AI 工具及使用方式,涵盖需求文档分析、技术文档编写、编程、PR/CR 和技术调研等工作内容,为提升工作效率提供了有力支持。作为一名程序员, 我现在已经深刻的体会到了AI带来的巨大的工作提升 本文…

An Attentive Inductive Bias for Sequential Recommendation beyond the Self-Attention

目录概符号说明BSARec (Beyond Self-Attention for Sequential Recommendation)代码Shin Y., Choi J., Wi H. and Park N. An attentive inductive bias for sequential recommendation beyond the self-attention. AAAI, 2024.概 本文在 attention block 中引入高低频滤波. 符…

[Leetcode]经典算法

检测环 快慢指针法是一种用于检测链表中是否存在环的有效方法,同时也可以找到环的起点。该方法的原理基于两个指针在链表上同时移动,其中一个移动得更快,而另一个移动得更慢。检测环的存在:使用两个指针,一个称为快指针(fast),一个称为慢指针(slow)。 在每一步中,快…

关于import multiprocessing引用出错

关于import multiprocessing引用出错 0. 原因 当前文件名与python包体中关键词出现同名,导致循环引用 1. 排查过程 问题代码 import timefrom multiprocessing import Process, Queue # 这里提示错误def producer(queue):queue.put("a")time.sleep(2)def consumer(q…