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

  • UWP/WinUI3 Win2d PixelShaderEffect 实现GradientMappingEffect (渐变映射滤镜)。已关闭评论
  • 225 次浏览
  • A+
所属分类:.NET技术
摘要

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

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

  效果图:

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

 

  一.渐变映射流程

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

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

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

    流程图:

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

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

 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.声明变量

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

 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 大小的幕后绘制画布用于绘制渐变图;

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

 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上如下图:

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

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

 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(邻近)值;

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

 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

  五.全部代码

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

 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

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

  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     }

后台代码