草庐IT

UWP/WinUI3 Win2d PixelShaderEffect 实现GradientMappingEffect (渐变映射滤镜)。

吃饭/睡觉 2023-03-28 原文

  在上一篇文章UWP/WinUI3 Win2d PixelShaderEffect 实现ColorPlacementEffect (颜色替换) 滤镜。 - 吃饭/睡觉 - 博客园 (cnblogs.com)中实现了 ”颜色替换滤镜“,那么本文将制作一个“渐变映射滤镜”。

  效果图:

    

 

  一.渐变映射流程

    1.将像素转换成灰度(0~1);

    2.将转灰后的像素值 跟渐变图进行查找颜色;

    3.将查找后的颜色进行替换原有颜色

    流程图:

                  

 1 //定义输入源个数为2
 2 #define D2D_INPUT_COUNT 2
 3 //将第一张输入源设置为简单采样模式
 4 #define D2D_INPUT0_SIMPLE
 5 //将第二张输入源设置为复制采样模式
 6 #define D2D_INPUT1_COMPLEX
 7 //引入hlsl帮助程序库
 8 #include "d2d1effecthelpers.hlsli"
 9 
10 //定义一个将颜色转换成灰度的函数
11 float GetGray(float3 color)
12 {
13     return (color.r + color.g + color.b) / 3.0;
14 }
15 //程序入口
16 D2D_PS_ENTRY(main){
17     //获取当前像素颜色
18     float4 color = D2DGetInput(0);
19     //转换成灰度
20     float gray = GetGray(color.rgb);
21     //对渐变图(第二张输入位图)进行 百分比位置采样;
22     float3 targetColor = D2DSampleInput(1,float2(gray,0.5)).rgb;
23     return float4(targetColor,color.a);
24 }
GradientMappingEffect.hlsl

  二.hlsl 解析:

    1.在头部定义里,声明了改hlsl 需要两张位图输入源,并将第一张(源图)的采样模式设置为SIMPLE(简单),因为我们对第一张输入源只用到了 D2DGetInput(){获取当前像素的颜色}函数; 将第二章(渐变图)的采样模式设置为COMPLEX(复杂),因为需要用到 D2DSampleInput(n,uv){按位置百分比获取指定位置的像素}函数;

    2.在主程序函数里面,首先获取“源图”当前位置的像素,然后进行转成灰度,再根据灰度调用 D2DSampleInput 函数获取“渐变图”指定位置的像素颜色,然后返回颜色;

  三.编译

 

    1.如果不清楚怎么编译 hlsl供PixelShaderEffect使用的可以看这边文章:UWP/WinUI3 Win2d PixelShaderEffect 实现ThresholdEffect 滤镜。 - 吃饭/睡觉 - 博客园 (cnblogs.com)

  四.使用PixelShaderEffect 制作“渐变映射”效果:

    1.声明变量

 1 //渐变映射效果
 2         PixelShaderEffect effect;
 3         //源图
 4         CanvasBitmap bitmap;
 5         //渐变图
 6         CanvasRenderTarget gradientMap;
 7         //效果渲染图
 8         CanvasRenderTarget render;
 9         //渐变颜色列表
10         Color[] colorMap;
变量

    2.在画布创建资源事件,首先将编译好的hlsl二进制代码读入到内存并转换成字节数组用于初始化一个PixelShaderEffect 对象,并创建一个 宽高255*1 大小的幕后绘制画布用于绘制渐变图;

 1 canvas.CreateResources += async (s, e) =>
 2             {
 3                 //获取着色器二进制文件
 4                 StorageFile file = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///Shaders/GradientMappingEffect.bin"));
 5                 IBuffer buffer = await FileIO.ReadBufferAsync(file);
 6                 //转换成字节数组
 7                 var bytes = buffer.ToArray();
 8                 //用 字节数组 初始化一个 PixelShaderEffect 对象;
 9                 effect = new PixelShaderEffect(bytes);
10                 //初始化渐变位图
11                 gradientMap = new CanvasRenderTarget(canvas, 255, 1, 96);
12                 colorMap = CreateGradientColors();
13                 CreateGradientRender(colorMap);
14             };
CreateResources

    3.在选中图片的事件上 初始化一个跟源图一样大小的 幕后绘制画布,用于存储绘制效果图像。这个幕后绘制画布是用于先将效果绘制到这个临时画布上,然后再将这个临时画布绘制到屏幕上。备注:这里应该会有读者会问,为什么不将 PixelShaderEffect 效果直接绘制到canvas 上呢,要做这多此一举的步骤呢?因为PixelShaderEffect 会将整个画布的每个像素都会调用 hlsl 里面的执行过程,会出现我们预料之外的效果。直接将 effect 绘制到canvas上如下图:

  

 1 //选择图片
 2             selectPicture.Click += async (s, e) =>
 3             {
 4                 var file = await Ulit.SelectFileAsync(new List<string> { ".png", ".jpg" });
 5                 if (file == null)
 6                 {
 7                     render = null;
 8                     bitmap = null;
 9                 }
10                 else
11                 {
12                     bitmap = await CanvasBitmap.LoadAsync(canvas.Device, await file.OpenAsync(FileAccessMode.Read));
13                     //创建一个与源图大小一样的幕后绘制画布;用于存放绘制效果;
14                     render = new CanvasRenderTarget(canvas, bitmap.Size);
15                 }
16                 canvas.Invalidate();
17             };
选择图片

  4.绘制图像到画布:在绘制图像我们只需要将 “源图”和“渐变图” 赋值到effect的Source1,Source2上,并且需要将 “渐变图”的 Source2Interpolation 的插值模式设置为 NearestNeighbor(邻近)值;

 1 canvas.Draw += (s, e) =>
 2             {
 3                 //绘制黑白网格
 4                 Win2dUlit.DrawGridGraphics(e.DrawingSession, 100);
 5                 //判断effect 和位图是否为空
 6                 if (effect == null || bitmap == null)
 7                     return;
 8                 var element = (FrameworkElement)s;
 9                 float effectWidth = (float)element.ActualWidth;
10                 float effectHeight = (float)element.ActualHeight * 0.6f;
11                 float previewWidth = effectWidth;
12                 float previewHeight = (float)element.ActualHeight * 0.3f;
13                 //设置两张位图源
14                 effect.Source1 = bitmap;
15                 effect.Source2 = gradientMap;
16                 //设置映射图采样为 邻近采样,(默认为线性采样,效果会表现出意料之外)
17                 effect.Source2Interpolation = CanvasImageInterpolation.NearestNeighbor;
18                //将效果绘制到render上
19                 using (var ds = render.CreateDrawingSession())
20                 {
21                     ds.Clear(Colors.Transparent);
22                     ds.DrawImage(effect);
23                 }
24                 ////绘制效果图到画布上
25                 var effectTran = Win2dUlit.CalcutateImageCenteredTransform(effectWidth, effectHeight, bitmap.Size.Width, bitmap.Size.Height);
26                 effectTran.Source = render;
27                 //直接将效果绘制到画布上,错误效果
28                 //var effectTran = Win2dUlit.CalcutateImageCenteredTransform(effectWidth, effectHeight, bitmap.Size.Width, bitmap.Size.Height);
29                 //effectTran.Source = effect;
30                 //e.DrawingSession.DrawImage(effectTran);
31 
32                 //绘制原图
33                 var previewTran = Win2dUlit.CalcutateImageCenteredTransform(previewWidth, previewHeight, bitmap.Size.Width, bitmap.Size.Height);
34                 previewTran.Source = bitmap;
35                 e.DrawingSession.DrawImage(previewTran, 0, effectHeight);
36                 //绘制渐变图
37                 var gradientTran = Win2dUlit.CalcutateImageCenteredTransform(effectWidth, element.ActualHeight * 0.1, gradientMap.Size.Width, gradientMap.Size.Height);
38                 gradientTran.Source = gradientMap;
39                 var rect = new Rect(0, previewHeight + effectHeight, effectWidth, element.ActualHeight * 0.1);
40                 e.DrawingSession.DrawImage(gradientMap, rect);
41             };
Draw

  五.全部代码

 1 <Grid>
 2         <Grid.RowDefinitions>
 3             <RowDefinition></RowDefinition>
 4             <RowDefinition Height="auto"></RowDefinition>
 5         </Grid.RowDefinitions>
 6         <canvas:CanvasControl x:Name="canvas"></canvas:CanvasControl>
 7         <StackPanel Grid.Row="1">
 8             <Button Content="选择图片" x:Name="selectPicture"></Button>
 9             <Button Content="创建渐变" x:Name="createGradient"></Button>
10         </StackPanel>
11     </Grid>
View Code
  1 public sealed partial class GradientMappingPage : Page
  2     {
  3         //渐变映射效果
  4         PixelShaderEffect effect;
  5         //源图
  6         CanvasBitmap bitmap;
  7         //渐变图
  8         CanvasRenderTarget gradientMap;
  9         //效果渲染图
 10         CanvasRenderTarget render;
 11         //渐变颜色列表
 12         Color[] colorMap;
 13         public GradientMappingPage()
 14         {
 15             this.InitializeComponent();
 16             Init();
 17         }
 18 
 19         void Init()
 20         {
 21             canvas.CreateResources += async (s, e) =>
 22             {
 23                 //获取着色器二进制文件
 24                 StorageFile file = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///Shaders/GradientMappingEffect.bin"));
 25                 IBuffer buffer = await FileIO.ReadBufferAsync(file);
 26                 //转换成字节数组
 27                 var bytes = buffer.ToArray();
 28                 //用 字节数组 初始化一个 PixelShaderEffect 对象;
 29                 effect = new PixelShaderEffect(bytes);
 30                 //初始化渐变位图
 31                 gradientMap = new CanvasRenderTarget(canvas, 255, 1, 96);
 32                 colorMap = CreateGradientColors();
 33                 CreateGradientRender(colorMap);
 34             };
 35 
 36             //选择图片
 37             selectPicture.Click += async (s, e) =>
 38             {
 39                 var file = await Ulit.SelectFileAsync(new List<string> { ".png", ".jpg" });
 40                 if (file == null)
 41                 {
 42                     render = null;
 43                     bitmap = null;
 44                 }
 45                 else
 46                 {
 47                     bitmap = await CanvasBitmap.LoadAsync(canvas.Device, await file.OpenAsync(FileAccessMode.Read));
 48                     //创建一个与源图大小一样的幕后绘制画布;用于存放绘制效果;
 49                     render = new CanvasRenderTarget(canvas, bitmap.Size);
 50                 }
 51                 canvas.Invalidate();
 52             };
 53 
 54             canvas.Draw += (s, e) =>
 55             {
 56                 //绘制黑白网格
 57                 Win2dUlit.DrawGridGraphics(e.DrawingSession, 100);
 58                 //判断effect 和位图是否为空
 59                 if (effect == null || bitmap == null)
 60                     return;
 61                 var element = (FrameworkElement)s;
 62                 float effectWidth = (float)element.ActualWidth;
 63                 float effectHeight = (float)element.ActualHeight * 0.6f;
 64                 float previewWidth = effectWidth;
 65                 float previewHeight = (float)element.ActualHeight * 0.3f;
 66                 //设置两张位图源
 67                 effect.Source1 = bitmap;
 68                 effect.Source2 = gradientMap;
 69                 //设置映射图采样为 邻近采样,(默认为线性采样,效果会表现出意料之外)
 70                 effect.Source2Interpolation = CanvasImageInterpolation.NearestNeighbor;
 71                //将效果绘制到render上
 72                 using (var ds = render.CreateDrawingSession())
 73                 {
 74                     ds.Clear(Colors.Transparent);
 75                     ds.DrawImage(effect);
 76                 }
 77                 ////绘制效果图到画布上
 78                 var effectTran = Win2dUlit.CalcutateImageCenteredTransform(effectWidth, effectHeight, bitmap.Size.Width, bitmap.Size.Height);
 79                 effectTran.Source = render;
 80                 //直接将效果绘制到画布上,错误效果
 81                 //var effectTran = Win2dUlit.CalcutateImageCenteredTransform(effectWidth, effectHeight, bitmap.Size.Width, bitmap.Size.Height);
 82                 //effectTran.Source = effect;
 83                 //e.DrawingSession.DrawImage(effectTran);
 84 
 85                 //绘制原图
 86                 var previewTran = Win2dUlit.CalcutateImageCenteredTransform(previewWidth, previewHeight, bitmap.Size.Width, bitmap.Size.Height);
 87                 previewTran.Source = bitmap;
 88                 e.DrawingSession.DrawImage(previewTran, 0, effectHeight);
 89                 //绘制渐变图
 90                 var gradientTran = Win2dUlit.CalcutateImageCenteredTransform(effectWidth, element.ActualHeight * 0.1, gradientMap.Size.Width, gradientMap.Size.Height);
 91                 gradientTran.Source = gradientMap;
 92                 var rect = new Rect(0, previewHeight + effectHeight, effectWidth, element.ActualHeight * 0.1);
 93                 e.DrawingSession.DrawImage(gradientMap, rect);
 94             };
 95 
 96             createGradient.Click += (s, e) =>
 97             {
 98                 colorMap = CreateGradientColors();
 99                 CreateGradientRender(colorMap);
100                 canvas.Invalidate();
101             };
102 
103         }
104         /// <summary>
105         /// 创建渐变颜色数组
106         /// </summary>
107         /// <returns></returns>
108         Color[] CreateGradientColors()
109         {
110             var count = Random.Shared.Next(3, 10);
111             var colors = new Color[count];
112             for (int i = 0; i < count; i++)
113             {
114                 var r = (byte)Random.Shared.Next(255);
115                 var g = (byte)Random.Shared.Next(255);
116                 var b = (byte)Random.Shared.Next(255);
117                 colors[i] = Color.FromArgb(255, r, g, b);
118             }
119             return colors;
120         }
121 
122         /// <summary>
123         /// 绘制渐变位图
124         /// </summary>
125         /// <param name="mapColors"></param>
126         void CreateGradientRender(Color[] mapColors)
127         {
128             using (var ds = gradientMap.CreateDrawingSession())
129             {
130                 var canvasGradienStop = new CanvasGradientStop[mapColors.Length];
131                 for (int i = 0; i < mapColors.Length; i++)
132                 {
133                     canvasGradienStop[i] = new CanvasGradientStop
134                     {
135                         Color = mapColors[i],
136                         Position = (float)i / (float)(mapColors.Length - 1)
137                     };
138                 }
139                 CanvasLinearGradientBrush linearBrush = new CanvasLinearGradientBrush(gradientMap.Device, canvasGradienStop)
140                 {
141                     StartPoint = new Vector2(0, 0),
142                     EndPoint = new Vector2(256, 0),
143                 };
144                 ds.FillRectangle(0, 0, 256, 1, linearBrush);
145             }
146         }
147     }
后台代码

 

GradientMappingEffectPixelShaderEffectspancolorstyle.NET技术

有关UWP/WinUI3 Win2d PixelShaderEffect 实现GradientMappingEffect (渐变映射滤镜)。的更多相关文章

  1. u盘安装系统(win10为例) - 2

    下载微PE工具箱进入官网下载微PE工具箱-下载 安装好后,打开微PE工具箱客户端,选择安装PE到U盘 PE壁纸可选择自己喜欢的壁纸,勾选上包含DOS工具箱,个性化盘符图标 下载原版系统进入网站下载镜像NEXT,ITELLYOU如果没有账号,注册一下就好进入选择开始使用选择win10 这里我们选择消费者版,用迅雷把BT种子下载下来 下面的两个盘符,是PE工具箱安装进U盘后,分成的盘符,注意EFI的盘符,这里面不能删东西,也不能添东西,另一个盘符可以当做正常的U盘空间使用,我们现在需要把下载下来的景象文件复制到正常的U盘空间中去 这个时候我们的系统U盘就只做好了 安装系统我们将U盘插入电脑,开机,

  2. Win10 / 11新电脑最简单跳过联网激活和使用本地账户登录方法 - 2

    跳过联网激活:OOBE界面直接按Ctrl+Shift+F3进入审核模式。这样就可以直接进入系统进行一些硬件测试等,而不用联网激活导致新机无法退货。需要注意的是,在审核模式下进行的一些操作都会保留,并不会在退出后自动还原!安装的软件在正常开机进系统后还会看见!如果电脑确实没连互联网又不想强行跳过OOBE(网上很多教程会叫你直接结束OOBE进程,但这是不推荐的,因为一些厂商自带优化程序和系统初始化设置在后面都会应用,对于笔记本跳过的话你会发现驱动和内置应用都没有装上。其实这部分脚本就在系统盘的Recovery隐藏文件夹下),可以参考以下方式:https://www.landiannews.com/

  3. ruby - 安装gem : Couldn't reserve space for cygwin's heap, Win32错误487错误 - 2

    我正在尝试在我的机器上安装win32-apigem,但在构建native扩展时我遇到了一些问题:$geminstallwin32-api--no-ri--rdocTemporarilyenhancingPATHtoincludeDevKit...Buildingnativeextensions.Thiscouldtakeawhile...C:\Programs\dev_kit\bin\make.exe:***Couldn'treservespaceforcygwin'sheap,Win32error0ERROR:Errorinstallingwin32-api:ERROR:Failed

  4. Ruby 1.9 - 没有这样的文件可以加载 'win32/open3' - 2

    我在Windows上运行ruby​​1.9.2并试图移植在Ruby1.8中工作的代码。该代码使用以前运行良好的Open4.popen4。对于1.9.2,我做了以下事情:通过geminstallPOpen4安装了POpen4需要POpen4通过require'popen4'尝试像这样使用POpen4:Open4.popen4("cmd"){|io_in,io_out,io_er|...}当我这样做时,我得到了错误:nosuchfiletoload--win32/open3如果我尝试安装win32-open3(geminstallwin32-open3),我会收到错误消息:win32-op

  5. 使用Python Win32COM如何获取对图表数据表的引用? - 2

    使用PythonWin32COM如何获取对图表数据表的引用?我可以使用数据表创建图表(PowerPoint将其弹出在单独的窗口中),例如:importwin32comfromMSOimportconstantsasmsoconstApplication=win32com.client.Dispatch("PowerPoint.Application")Application.Visible=TruePresentation=Application.Presentations.Add()FirstSlide=Presentation.Slides.Add(1,12)...noproblemadd

  6. 如何使用蓝牙连接将字符从UWP应用程序传输到Android应用? - 2

    我正在为RaspberryPi开发其UWP应用程序的应用程序,因此我想从UWP应用程序发送和接收字符到Android应用程序。因此,请告诉我什么是UWP应用程序的代码。提前致谢:)看答案此站点向您展示如何发送和接收串行蓝牙数据。github上的来源

  7. win10系统下Edge浏览器搜索引擎[必应]和新建标签页被篡改百度的一种解决方式 - 2

    一、我的情况:win10系统下Edge浏览器搜索引擎[必应]和新建标签页被篡改百度的搜索引擎和百度的页面我的解决方案步骤如下:1.检查电脑管家的浏览器保护检查一下你的电脑是否后台开启了电脑管家;如果是,则检查一下是否开启了浏览器保护。由于我是联想的笔记本,自带联想管家,一直没有关闭过它,以我的电脑为例,进行关闭,如下图所示。其他的电脑管家软件,应该也有类似的功能,耐心找一下就能找到啦。2.在Edge浏览中进行相关设置先点击浏览器右上角的三个点,然后找到“设置”,然后进入设置界面。在当前页面手动搜索“搜索引擎”,然后选择需要的搜索引擎“必应”,然后点击管理搜索引擎。进入管理搜索引擎界面后,理论上

  8. 【修电脑】VMware 从GHO文件备份恢复Win10/Win7系统 - 2

    【修电脑】VMware从GHO文件备份恢复Win10/Win7系统注意参考硬盘知识一、硬盘接口的分类二、硬盘的分类按照硬盘材质分为两大类按照接口类型区分boot启动知识LegacyBIOS引导uefi引导启动流程查看系统的引导启动方式1.VMware新建win10x64系统2.制作老毛桃U盘winpe3.VMwarewin10从winpe老毛桃U盘启动4.GHO文件还原系统(失败,勿复现)5.GHO转vmdk6.成功实现经验总结注意本文仅供参考学习,任何因阅读者操作导致的数据损失和破坏,本文作者概不负责!参考GHOST文件如何导入虚拟机硬盘知识一、硬盘接口的分类硬盘接口通常分为五种类型:SAT

  9. win 10 强制禁用驱动程序签名 - 2

    文章目录方法1,通过组策略禁用(未验证)1、首先按下键盘“Win+R”打开运行。2、接着在其中输入“gpedit.msc”回车打开组策略。3、然后进入计算机配置下“用户配置”中的“管理模板”4、再打开“系统”下的“驱动程序安装”5、进入后,双击打开其中的“设备驱动程序的代码签名”6、最后勾选“已启用”并将选项改成“忽略”再确定保存即可禁用强制签名。方法2(临时禁用)1、打开并登录操作系统左下角。开始菜单上单击选择设置。2、在Windows设置页面选择更新和安全。3、在更新和安全页面选择左侧的恢复标签,在右侧选择立即重新启动。4、在新的启动页面选择疑难解答。5、在疑难解答页面选择高级选项。6、在

  10. javascript - 获取 OS Win 7 用户名 Javascript - 2

    是否可以在浏览器IE、Chrome、Firefox、Opera中使用Javascript获取Windows用户名和PCName? 最佳答案 没有。此类信息不会暴露给浏览器中的javascript引擎。 关于javascript-获取OSWin7用户名Javascript,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/8802602/

随机推荐