UWP/WinUI3 Win2d PixelShaderEffect 实现ColorPlacementEffect (颜色替换) 滤镜。
在上一篇:UWP/WinUI3 PixelShaderEffect 实现ThresholdEffect 滤镜。 - 吃饭/睡觉 - 博客园 (cnblogs.com) 已经价绍了如何编写hsls,编译,和使用 PixelShaderEffect 来实现自定义滤镜效果了,那么本编将介绍如何编写一个 “颜色替换滤镜”;
效果图:
一.颜色匹配理论
1.根据指定的颜色和阈值匹配到 指定颜色范围的像素。
2.利用hsv颜色模式 调整选中像素的 色调,饱和度,亮度;
//设置输入源数量 #define D2D_INPUT_COUNT 1 //将输入源0 设置为简单采样模式 #define D2D_INPUT0_SIMPLE //引入hlsl帮助程序 #include "d2d1effecthelpers.hlsli" //定义属性 //源颜色 float(0,0,0)~float(1,1,1) float3 sourceColor; //替换后的颜色,hsv颜色 float(0,0,0)~float(1,1,1) float3 hsv; //颜色容差 0~1 float threshold; //HSV 到 RGB 转换 float3 hsv2rgb(float3 c) { float3 rgb = clamp(abs(fmod(c.x * 6.0 + float3(0.0, 4.0, 2.0), 6.0) - 3.0) - 1.0, 0.0, 1.0); float3 f1 = float3(1,1,1); return c.z * lerp(f1, rgb, float3(c.y,c.y,c.y)); //return f1; } //rgb到hsv 转换 float3 rgb2hsv(float3 c) { float4 K = float4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); float a = step(c.b, c.g); float4 p = lerp(float4(c.bg, K.wz), float4(c.gb, K.xy), float4(a,a,a,a)); float b = step(p.x, c.r); float4 q = lerp(float4(p.xyw, c.r), float4(c.r, p.yzx), float4(b,b,b,b)); float d = q.x - min(q.w, q.y); float e = 1.0e-10; return float3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); } //主程序 D2D_PS_ENTRY(main){ //获取当前像素颜色 float4 sColor = D2DGetInput(0); float3 color = sColor.rgb; //当前像素的每个分量减去输入颜色的对于分量的绝对值跟阈值做比较,查看是否符合条件 if(abs(color.r - sourceColor.r) < threshold && abs(color.g - sourceColor.g) < threshold && abs(color.b - sourceColor.b) < threshold) { //将当前颜色从 rgb转到hsv float3 hsv = rgb2hsv(color); //声明 计算后的hsv 变量 float3 thsv = float3(0,0,0); //虽然这里用的都是rgb属性,但是对应的是hsv //当前像素的hsv与输入的hsv相加 thsv.r= fmod(hsv.r*360+hsv.r*360,360)/360.0; thsv.g= clamp(hsv.g+hsv.g,0,1); thsv.b= clamp(hsv.b+hsv.b,0,1); //将累计后的hsv 转换成rgb然后输出颜色 float3 resultColor = hsv2rgb(thsv); return float4(resultColor,sColor.a); } //不符合条件的像素,返回输入的颜色即可 return sColor; }
1).在代码里定义了三个属性
1.sourceColor : 源颜色,用于匹配输入的颜色像素;
2.hsv : 输入的hsv 颜色,用于跟符合条件的像素进行累加计算。(在这里我将hsv的三个分量的范围都设置成了-1~1之间了,因为使用到的两个转换函数返回的值都是在0~1之间的。)
3.threshold: 阈值范围;
2).在代码中定义了两个 颜色模式转换的函数,因为这两个函数是我从别的地方复制过来的所以我也不是很懂。
颜色转换引用:Unity Shader - HSV 和 RGB 的相互转换 - 知乎 (zhihu.com)
二.选区效果
从效果图中可以看到我在图像的右下角显示了一个显示当前选中像素的效果图,其实这个也是用hlsl 绘制出来了,在这里也将这个hlsl 贴出来,因为代码上已经写了注释了,在这里就不详细讲了;
1 //声明只有一张纹理输入 2 #define D2D_INPUT_COUNT 1 3 //将第一张纹理设置为简单模式采样 4 #define D2D_INPUT0_SIMPLE 5 //引入hlsl 帮助程序库 6 #include "d2d1effecthelpers.hlsli" 7 8 //输入颜色,用户配备 9 float3 color; 10 //阈值 11 float threshold; 12 13 // HSV 到 RGB 转换 14 float3 hsv2rgb(float3 c) 15 { 16 float3 rgb = clamp(abs(fmod(c.x * 6.0 + float3(0.0, 4.0, 2.0), 6.0) - 3.0) - 1.0, 0.0, 1.0); 17 float3 f1 = float3(1, 1, 1); 18 return c.z * lerp(f1, rgb, float3(c.y, c.y, c.y)); 19 } 20 21 D2D_PS_ENTRY(main){ 22 //获取当前像素颜色 23 float4 sColor = D2DGetInput(0); 24 //将当前像素的rgb每个分量减去输入颜色的每个分量 25 //用于跟阈值对比,查看当前像素是否符合条件 26 float r = abs(sColor.r - color.r); 27 float g = abs(sColor.g - color.g); 28 float b = abs(sColor.b - color.b); 29 if( r< threshold && g < threshold && b < threshold) 30 { 31 //将色相和饱和度都设置为0 32 //将rgb三个分量相加后除以阈值*3 计算出比率然后设置当前像素的亮度 33 float3 hsv = float3(0, 0, 0); 34 hsv.b= 1-(r+g+b)/(threshold*3); 35 //用hsv2rgb 将hsv 转换成rgb颜色 36 float3 tColor = hsv2rgb(hsv); 37 return float4(tColor,sColor.a); 38 } 39 //不符合条件的像素返回黑色 40 return float4(0,0,0,1); 41 }
三.绘制方法,传入参数设定
1 canvas.Draw += (s, e) => 2 { 3 //绘制黑白网格 4 Win2dUlit.DrawGridGraphics(e.DrawingSession, 100); 5 //判断effect 和位图是否为空 6 if (effect == null || bitmap == null) 7 return; 8 if (color != null) 9 { 10 //绘制替换颜色效果 11 //颜色将0~255 转换成0~1 因为hlsl里面的颜色是0~1范围的并且是float类型,所以这里需要将每个颜色的分量除以255 12 Vector3 hsv = targetHsv.HsvToVector3(); 13 Vector3 sourceColor = new Vector3(color.Value.R / 255f, color.Value.G / 255f, color.Value.B / 255f); 14 //通过键值对的形式设置属性传递到着色器里面 15 effect.Properties["threshold"] = (float)threshold.Value; 16 effect.Properties["hsv"] = hsv; 17 effect.Properties["sourceColor"] = sourceColor; 18 effect.Source1 = bitmap; 19 //绘制效果图 20 var effectTran = Win2dUlit.CalcutateImageCenteredTransform(effectWidth, effectHeight, bitmap.Size.Width, bitmap.Size.Height); 21 effectTran.Source = effect; 22 e.DrawingSession.DrawImage(effectTran); 23 //绘制选区图 右下角 24 var constTran = Win2dUlit.CalcutateImageCenteredTransform(previewWidth, previewHeight, bitmap.Size.Width, bitmap.Size.Height); 25 constEffect.Source1 = bitmap; 26 constEffect.Properties["threshold"] = (float)threshold.Value; 27 constEffect.Properties["color"] = sourceColor; 28 using (var ds = constRender.CreateDrawingSession()) 29 { 30 ds.Clear(Colors.Transparent); 31 ds.DrawImage(constEffect, new Rect(new Point(), bitmap.Size), new Rect(new Point(), bitmap.Size)); 32 } 33 constTran.Source = constRender; 34 e.DrawingSession.DrawImage(constTran, previewWidth, effectHeight); 35 } 36 else 37 { 38 var effectTran = Win2dUlit.CalcutateImageCenteredTransform(effectWidth, effectHeight, bitmap.Size.Width, bitmap.Size.Height); 39 effectTran.Source = bitmap; 40 e.DrawingSession.DrawImage(effectTran); 41 } 42 //绘制原图 左下角 43 var previewTran = Win2dUlit.CalcutateImageCenteredTransform(previewWidth, previewHeight, bitmap.Size.Width, bitmap.Size.Height); 44 previewTran.Source = bitmap; 45 e.DrawingSession.DrawImage(previewTran, 0, effectHeight); 46 //绘制选择颜色缩略图 47 if (isPressed && (pos.X >= offsetX && pos.X <= offsetX + width && pos.Y >= offsetY && pos.Y <= offsetY + height)) 48 { 49 //画三角形 50 CanvasPathBuilder builder = new CanvasPathBuilder(canvas); 51 builder.BeginFigure(pos); 52 builder.AddLine(pos - new Vector2(5, 5)); 53 builder.AddLine(pos - new Vector2(-5, 5)); 54 builder.EndFigure(CanvasFigureLoop.Closed); 55 CanvasGeometry geometry = CanvasGeometry.CreatePath(builder); 56 e.DrawingSession.FillGeometry(geometry, Colors.White); 57 e.DrawingSession.FillRoundedRectangle(new Rect(pos.X - 50, pos.Y - 100 - 5, 100, 100), 10, 10, Colors.White); 58 //采样位置 59 var usePx = (pos.X - offsetX) / scale; 60 var usePy = (pos.Y - offsetY) / scale; 61 e.DrawingSession.DrawImage(bitmap, pos.X - 50, pos.Y - 100 - 5, new Rect(usePx - 50, usePy - 50, 100, 100)); 62 e.DrawingSession.DrawRectangle(new Rect(pos.X - 1, pos.Y - 56, 3, 3), Colors.Orange); 63 e.DrawingSession.DrawRoundedRectangle(new Rect(pos.X - 50, pos.Y - 100 - 5, 100, 100), 10, 10, Colors.Black); 64 } 65 };
在绘制方法里面我们只需要关心如和传入参数和参数类型以及范围
1.sourceColor: 通过键值对的形式设置效果的参数,但是在传入颜色之前需要 从rgb(0~255) 转换成 vector3(0~1);
2.hsv :在传入前需要将hsv颜色 从 (360,100,100)转换成 (1,1,1)
3.threshold: 因为在hlsl 上定义的是float 类型,所以传入时 也需要将值转换成float类型才能传入;
四.所有代码
因为下面的很多代码都是关于一些输入交互的所以这里直接将代码贴出来,复制到已经引用win2d库的项目应该就能运行了。
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 <Slider Header="阈值:" Value="0.2" Maximum="1" StepFrequency="0.01" x:Name="threshold"></Slider> 10 <StackPanel Orientation="Horizontal"> 11 <Border Margin="10 0 10 0" x:Name="selectColor" BorderBrush="Black" BorderThickness="2"> 12 <TextBlock Text="选择的颜色" HorizontalAlignment="Center" VerticalAlignment="Center" ></TextBlock> 13 </Border> 14 <StackPanel Width="300"> 15 <Rectangle Width="200" Fill="Gray" Height="30" x:Name="replaceColor"></Rectangle> 16 <Slider Header="色相" x:Name="hue" StepFrequency="1" Minimum="-180" Maximum="180"></Slider> 17 <Slider Header="饱和度" x:Name="saturation" Minimum="-100" Maximum="100"></Slider> 18 <Slider Header="亮度" x:Name="brightness" Minimum="-100" Maximum="100"></Slider> 19 </StackPanel> 20 </StackPanel> 21 22 </StackPanel> 23 </Grid>
1 public sealed partial class ColorReplacementPage : Page 2 { 3 PixelShaderEffect effect; 4 //选区效果 5 PixelShaderEffect constEffect; 6 CanvasRenderTarget constRender; 7 CanvasBitmap bitmap; 8 Color[] bitmapColors; 9 public ColorReplacementPage() 10 { 11 this.InitializeComponent(); 12 Init(); 13 InitOperate(); 14 } 15 Hsv targetHsv = new Hsv(); 16 Hsv sourceHsv = new Hsv(); 17 Color? color; 18 float effectWidth; 19 float effectHeight; 20 float previewWidth; 21 float previewHeight; 22 //记录鼠标是否在画布上按下 23 bool isPressed; 24 //记录小图在画布上的大小和位置,和偏移 25 float offsetX, offsetY, scale, width, height; 26 Vector2 pos; 27 void Init() 28 { 29 canvas.CreateResources += async (s, e) => 30 { 31 //获取着色器二进制文件 32 StorageFile file = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///Shaders/ColorReplacementEffect.bin")); 33 IBuffer buffer = await FileIO.ReadBufferAsync(file); 34 //转换成字节数组 35 var bytes = buffer.ToArray(); 36 //用 字节数组 初始化一个 PixelShaderEffect 对象;b v 37 effect = new PixelShaderEffect(bytes); 38 39 file = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///Shaders/ConstituencyEffect.bin")); 40 buffer = await FileIO.ReadBufferAsync(file); 41 bytes = buffer.ToArray(); 42 constEffect = new PixelShaderEffect(bytes); 43 }; 44 //选择图片 45 selectPicture.Click += async (s, e) => 46 { 47 var file = await Ulit.SelectFileAsync(new List<string> { ".png", ".jpg" }); 48 if (file == null) 49 { 50 bitmap = null; 51 color = null; 52 } 53 else 54 { 55 bitmap = await CanvasBitmap.LoadAsync(canvas.Device, await file.OpenAsync(FileAccessMode.Read)); 56 //获取图像像素数组 57 bitmapColors = bitmap.GetPixelColors(); 58 constRender = new CanvasRenderTarget(canvas, bitmap.Size); 59 } 60 CalcuationDrawPosition(); 61 canvas.Invalidate(); 62 }; 63 canvas.Draw += (s, e) => 64 { 65 //绘制黑白网格 66 Win2dUlit.DrawGridGraphics(e.DrawingSession, 100); 67 //判断effect 和位图是否为空 68 if (effect == null || bitmap == null) 69 return; 70 if (color != null) 71 { 72 //绘制替换颜色效果 73 //颜色将0~255 转换成0~1 因为hlsl里面的颜色是0~1范围的并且是float类型,所以这里需要将每个颜色的分量除以255 74 Vector3 hsv = targetHsv.HsvToVector3(); 75 Vector3 sourceColor = new Vector3(color.Value.R / 255f, color.Value.G / 255f, color.Value.B / 255f); 76 //通过键值对的形式设置属性传递到着色器里面 77 effect.Properties["threshold"] = (float)threshold.Value; 78 effect.Properties["hsv"] = hsv; 79 effect.Properties["sourceColor"] = sourceColor; 80 effect.Source1 = bitmap; 81 //绘制效果图 82 var effectTran = Win2dUlit.CalcutateImageCenteredTransform(effectWidth, effectHeight, bitmap.Size.Width, bitmap.Size.Height); 83 effectTran.Source = effect; 84 e.DrawingSession.DrawImage(effectTran); 85 //绘制选区图 右下角 86 var constTran = Win2dUlit.CalcutateImageCenteredTransform(previewWidth, previewHeight, bitmap.Size.Width, bitmap.Size.Height); 87 constEffect.Source1 = bitmap; 88 constEffect.Properties["threshold"] = (float)threshold.Value; 89 constEffect.Properties["color"] = sourceColor; 90 using (var ds = constRender.CreateDrawingSession()) 91 { 92 ds.Clear(Colors.Transparent); 93 ds.DrawImage(constEffect, new Rect(new Point(), bitmap.Size), new Rect(new Point(), bitmap.Size)); 94 } 95 constTran.Source = constRender; 96 e.DrawingSession.DrawImage(constTran, previewWidth, effectHeight); 97 } 98 else 99 { 100 var effectTran = Win2dUlit.CalcutateImageCenteredTransform(effectWidth, effectHeight, bitmap.Size.Width, bitmap.Size.Height); 101 effectTran.Source = bitmap; 102 e.DrawingSession.DrawImage(effectTran); 103 } 104 //绘制原图 左下角 105 var previewTran = Win2dUlit.CalcutateImageCenteredTransform(previewWidth, previewHeight, bitmap.Size.Width, bitmap.Size.Height); 106 previewTran.Source = bitmap; 107 e.DrawingSession.DrawImage(previewTran, 0, effectHeight); 108 //绘制选择颜色缩略图 109 if (isPressed && (pos.X >= offsetX && pos.X <= offsetX + width && pos.Y >= offsetY && pos.Y <= offsetY + height)) 110 { 111 //画三角形 112 CanvasPathBuilder builder = new CanvasPathBuilder(canvas); 113 builder.BeginFigure(pos); 114 builder.AddLine(pos - new Vector2(5, 5)); 115 builder.AddLine(pos - new Vector2(-5, 5)); 116 builder.EndFigure(CanvasFigureLoop.Closed); 117 CanvasGeometry geometry = CanvasGeometry.CreatePath(builder); 118 e.DrawingSession.FillGeometry(geometry, Colors.White); 119 e.DrawingSession.FillRoundedRectangle(new Rect(pos.X - 50, pos.Y - 100 - 5, 100, 100), 10, 10, Colors.White); 120 //采样位置 121 var usePx = (pos.X - offsetX) / scale; 122 var usePy = (pos.Y - offsetY) / scale; 123 e.DrawingSession.DrawImage(bitmap, pos.X - 50, pos.Y - 100 - 5, new Rect(usePx - 50, usePy - 50, 100, 100)); 124 e.DrawingSession.DrawRectangle(new Rect(pos.X - 1, pos.Y - 56, 3, 3), Colors.Orange); 125 e.DrawingSession.DrawRoundedRectangle(new Rect(pos.X - 50, pos.Y - 100 - 5, 100, 100), 10, 10, Colors.Black); 126 } 127 }; 128 threshold.ValueChanged += (s, e) => canvas.Invalidate(); 129 RangeBaseValueChangedEventHandler hsvChanged = (s, e) => 130 { 131 if (color == null) 132 return; 133 134 if (s is Slider slider) 135 { 136 switch (slider.Name) 137 { 138 case "hue": 139 targetHsv.H = e.NewValue; 140 break; 141 case "saturation": 142 targetHsv.S = e.NewValue; 143 break; 144 case "brightness": 145 targetHsv.V = e.NewValue; 146 break; 147 } 148 replaceColor.Fill = new SolidColorBrush((sourceHsv + targetHsv).HsvToRgb()); 149 } 150 canvas.Invalidate(); 151 }; 152 hue.ValueChanged += hsvChanged; 153 saturation.ValueChanged += hsvChanged; 154 brightness.ValueChanged += hsvChanged; 155 } 156 157 void InitOperate() 158 { 159 //画布大小更改,计算绘制区域 160 canvas.SizeChanged += (s, e) => CalcuationDrawPosition(); 161 //鼠标再画布按下 162 canvas.PointerPressed += (s, e) => 163 { 164 isPressed = true; 165 canvas.Invalidate(); 166 }; 167 //鼠标松开 168 canvas.PointerReleased += (s, e) => 169 { 170 isPressed = false; 171 canvas.Invalidate(); 172 }; 173 174 canvas.PointerMoved += (s, e) => 175 { 176 if (!isPressed || bitmap == null) 177 return; 178 Point p = e.GetCurrentPoint(canvas).Position; 179 pos = p.ToVector2(); 180 //是否进入范围 181 if ((p.X >= offsetX && p.X <= offsetX + width && p.Y >= offsetY && p.Y <= offsetY + height)) 182 { 183 var x = p.X - offsetX; 184 var y = p.Y - offsetY; 185 x = x / scale; 186 y = y / scale; 187 color = bitmapColors[(int)x + (int)y * bitmap.SizeInPixels.Width]; 188 sourceHsv = sourceHsv.RgbToHsv(color.Value); 189 replaceColor.Fill = new SolidColorBrush((sourceHsv + targetHsv).HsvToRgb()); 190 //设置选中的颜色 191 selectColor.Background = new SolidColorBrush(color.Value); 192 canvas.Invalidate(); 193 } 194 }; 195 } 196 void CalcuationDrawPosition() 197 { 198 //判断effect 和位图是否为空 199 if (effect == null || bitmap == null) 200 return; 201 var element = canvas; 202 effectWidth = (float)element.ActualWidth; 203 effectHeight = (float)element.ActualHeight * 0.7f; 204 previewWidth = effectWidth*0.5f; 205 previewHeight = (float)element.ActualHeight * 0.3f; 206 var mat = Win2dUlit.CalcutateImageCenteredMat(previewWidth, previewHeight, bitmap.Size.Width, bitmap.Size.Height); 207 scale = mat.M11; 208 offsetX = mat.M31; 209 offsetY = mat.M32 + effectHeight; 210 width = (float)(scale * bitmap.Size.Width); 211 height = (float)(scale * bitmap.Size.Height); 212 } 213 }
1 public static class Win2dUlit 2 { 3 public static void DrawGridGraphics(CanvasDrawingSession draw, int size = 100) 4 { 5 var target = new CanvasRenderTarget(draw, new Windows.Foundation.Size(size * 2, size * 2)); 6 using (var ds = target.CreateDrawingSession()) 7 { 8 for (int x = 0; x < 2; x++) 9 { 10 for (int y = 0; y < 2; y++) 11 { 12 Color color = (x + y) % 2 == 0 ? Colors.White : Color.FromArgb(255, 219, 219, 219); 13 ds.FillRectangle(new Rect(x * size, y * size, size, size), color); 14 } 15 } 16 } 17 var gridTargetEffect = new BorderEffect() 18 { 19 Source = target, 20 ExtendY = CanvasEdgeBehavior.Wrap, 21 ExtendX = CanvasEdgeBehavior.Wrap, 22 }; 23 draw.DrawImage(gridTargetEffect); 24 } 25 26 public static Transform2DEffect CalcutateImageCenteredTransform(double cWidth, double cHeight, double iWidth, double iHeight) 27 { 28 var mat = CalcutateImageCenteredMat(cWidth, cHeight, iWidth, iHeight); 29 return new Transform2DEffect() { TransformMatrix = mat }; 30 } 31 32 public static Transform2DEffect CalcutateImageCenteredTransform(Windows.Foundation.Size cSize, Windows.Foundation.Size iSize) 33 { 34 return CalcutateImageCenteredTransform(cSize.Width, cSize.Height, iSize.Width, iSize.Height); 35 } 36 37 public static Matrix3x2 CalcutateImageCenteredMat(Windows.Foundation.Size cSize, Windows.Foundation.Size iSize) 38 { 39 return CalcutateImageCenteredMat(cSize.Width, cSize.Height, iSize.Width, iSize.Height); 40 } 41 public static Matrix3x2 CalcutateImageCenteredMat(double cWidth, double cHeight, double iWidth, double iHeight) 42 { 43 float f = (float)Math.Min(cWidth / iWidth, cHeight / iHeight); 44 float ox = (float)(cWidth - iWidth * f) / 2; 45 float oy = (float)(cHeight - iHeight * f) / 2; 46 Matrix3x2 matrix3X2 = Matrix3x2.CreateScale(f) * Matrix3x2.CreateTranslation(ox, oy); 47 return matrix3X2; 48 } 49 }
1 public class Hsv 2 { 3 public Hsv() { } 4 public Hsv(double h, double s, double v) 5 { 6 H = h; 7 S = s; 8 V = v; 9 } 10 /// <summary> 11 /// 色相 0~360 12 /// </summary> 13 public double H { get;set; } 14 /// <summary> 15 /// 亮度 0~100 16 /// </summary> 17 public double S { get;set; } 18 /// <summary> 19 /// 饱和度 0~100 20 /// </summary> 21 public double V { get;set; } 22 23 /// <summary> 24 /// 将当前hsv转换成颜色返回 25 /// </summary> 26 public Color HsvToRgb() 27 { 28 double s = this.S / 100; 29 double v = this.V / 100; 30 double h = this.H; 31 double R = 0, G = 0, B = 0; 32 if (s == 0) 33 R = G = B = v; 34 else 35 h /= 60; 36 int i = (int)(h); 37 double f = h - i; 38 double a = v * (1 - s); 39 double b = v * (1 - s * f); 40 double c = v * (1 - s * (1 - f)); 41 switch (i) 42 { 43 case 0: 44 case 6: 45 R = v; G = c; B = a; 46 break; 47 case 1: 48 R = b; G = v; B = a; 49 break; 50 case 2: 51 R = a; G = v; B = c; 52 break; 53 case 3: 54 R = a; G = b; B = v; 55 break; 56 case 4: 57 R = c; G = a; B = v; 58 break; 59 case 5: 60 R = v; G = a; B = b; 61 break; 62 } 63 R *= 255; 64 G *= 255; 65 B *= 255; 66 Color color = Color.FromArgb(255, (byte)R, (byte)G, (byte)B); 67 return color; 68 } 69 public Hsv RgbToHsv(Color c) 70 { 71 int R, G, B; 72 R = c.R; 73 G = c.G; 74 B = c.B; 75 double V, S, H = 0; 76 double max = Math.Max(R, Math.Max(G, B)); 77 double min = Math.Min(R, Math.Min(G, B)); 78 var dalte = max - min; 79 V = max / 255; 80 S = dalte == 0 ? 0 : dalte / max; 81 if (R == max) H = (G - B) / (max - min) * 60; 82 if (G == max) H = 120 + (B - R) / (max - min) * 60; 83 if (B == max) H = 240 + (R - G) / (max - min) * 60; 84 if (dalte == 0) H = 0; 85 if (H < 0) 86 H = H + 360; 87 Hsv hsv = new Hsv() { H = (int)H, S = S * 100, V = V * 100 }; 88 return hsv; 89 } 90 public static Hsv operator +(Hsv a, Hsv b) 91 { 92 var h = (a.H + b.H) % 360; 93 var s = (a.S + b.S) % 100; 94 var v = (a.V + b.V) % 100; 95 return new Hsv(h, s, v); 96 } 97 public Vector3 HsvToVector3() 98 { 99 var x = (float)(this.H / 360.0); 100 var y = (float)(this.S / 100.0); 101 var z = (float)(this.V / 100.0); 102 return new Vector3(x, y, z); 103 } 104 } 105 }
五.总结
通过使用hlsl着色器代码,我们就可以实现自定义滤镜,来实现不同的效果。