- UI组件
- 桌面应用
【免费下载链接】HandyControl
Contains some simple and commonly used WPF controls
HandyControl 在HandyControl.Media.Effects命名空间下提供了一套基于 WPFShaderEffect(PixelShader 像素着色器)的特效组件,覆盖明度(Brightness)、补色(ColorComplement)、颜色矩阵(ColorMatrix)、对比度(Contrast)、灰度(GrayScale)等常见图像处理需求,并额外提供BlendEffectBox控件用于在同一个元素上叠加多个特效。阅读本文后,你将掌握每个特效的 XAML 用法、参数语义与取值影响,理解其底层 HLSL 着色器原理,并能直接参照官方 Demo 将特效组合落地到自己的 WPF 项目中。
特效体系概览:基于 PixelShader 的位图特效
HandyControl 的全部特效都派生自抽象基类 EffectBase.cs,而EffectBase又继承自 WPF 的ShaderEffect:
public abstract class EffectBase : ShaderEffect { public static readonly DependencyProperty InputProperty = RegisterPixelShaderSamplerProperty("Input", typeof(EffectBase), 0); public Brush Input { get; set; } }要点:
InputProperty通过RegisterPixelShaderSamplerProperty注册为采样器 0(register s0),即特效对上层传入的位图内容逐像素进行处理,属于标准的位图(Bitmap)特效,只能通过UIElement.Effect这类Effect属性挂载,而不是OpacityMask或Clip。- 每个特效类在静态构造函数中通过
pack://application:,,,/HandyControl;component/Resources/Effects/xxx.ps加载预编译的.ps着色器文件,编译产物与 HLSL 源码一并存放在 Resources/Effects 目录(每个特效各有一个.fx源文件与对应的.ps编译文件)。 - 参数均以依赖属性(DependencyProperty)实现,并通过
PixelShaderConstantCallback(0)等索引映射到着色器常量寄存器 C0、C1……,因此参数变化时 GPU 端会同步更新,无需重新编译着色器。
由于特效是通用ShaderEffect,除了官方文档演示的Image之外,任何UIElement(Border、Button、TextBlock等)都可以挂载使用,这也正是BlendEffectBox能够实现“任意内容 + 多特效叠加”的基础。
BlendEffectBox:在一个控件上叠加多个特效
WPF 的Effect属性同一时刻只能挂载一个特效对象,如果要同时应用模糊、明度、对比度等多个效果,常规做法是层层嵌套元素,非常繁琐。HandyControl 提供的BlendEffectBox将这一过程封装成声明式配置:
<hc:BlendEffectBox> <hc:BlendEffectBox.Effects> <BlurEffect Radius="30"/> <hc:BrightnessEffect Brightness="1"/> <hc:ContrastEffect Contrast="20"/> </hc:BlendEffectBox.Effects> <!-- 你的控件 --> </hc:BlendEffectBox>BlendEffectBox.Effects是一个ObservableCollection<Effect>,可以混用 HandyControl 的特效与 WPF 自带特效(如示例中的BlurEffect);BlendEffectBox的内容部分就是被施加特效的目标控件。
底层实现:ContentPresenter 链式嵌套
从源码 BlendEffectBox.cs 可以看到它的实现思路——用ContentPresenter递归嵌套来模拟“多层 Effect 叠加”:
- 构造函数中创建了两个关键 presenter:
_effectTopPresenter(对外暴露为ActualContent,同时把真实内容Content绑定到_effectBottomPresenter); - 当
Effects集合变化时,AddEffect按集合的逆序把每个特效逐层挂到ContentPresenter.Effect上,并把下一层 presenter 作为上一层的内容,从而形成特效N → 特效N-1 → … → 特效1 → 真实内容的渲染链条; ClearEffect则沿链条递归清空每一层的Effect并回收 presenter,集合为空时特效即被完全移除。
其默认样式定义在 BlendEffectBox.xaml:
<Style TargetType="hc:BlendEffectBox"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="hc:BlendEffectBox"> <ContentPresenter ContentSource="ActualContent"/> </ControlTemplate> </Setter.Value> </Setter> </Style>模板直接以ActualContent(即特效链顶层)作为呈现内容,因此BlendEffectBox本身外观透明,只负责“叠加特效”这一职责。使用中需要注意的是:Content的静态类型是FrameworkElement,目标内容必须是FrameworkElement的实例(StackPanel、Border、Image等均可)。
BrightnessEffect 明度特效
BrightnessEffect用于整体提亮或压暗画面,官方文档示例:
<Image Width="120" Height="120" Source="/HandyControlDemo;component/Resources/Img/Album/10.jpg"> <Image.Effect> <hc:BrightnessEffect Brightness="1"/> </Image.Effect> </Image>- 参数:
Brightness(double),默认值1,注册见 BrightnessEffect.cs 中的new PropertyMetadata(ValueBoxes.Double1Box, PixelShaderConstantCallback(0))。 - 取值影响:
Brightness = 1为原始画面;大于 1 时整体变亮,小于 1(如0.6)时整体变暗;取 0 则画面趋近于黑。 - 底层公式(BrightnessEffect.fx):
float4 color = tex2D(implicitInput, uv); color.rgba *= brightness; return color;注意它是对 RGBA 四个通道同时相乘,因此会连 alpha 一起缩放;一般建议Brightness取非负值。
ColorComplementEffect 补色特效
ColorComplementEffect不需要任何参数,直接对颜色求“补色”,示例:
<Image Width="120" Height="120" Source="/HandyControlDemo;component/Resources/Img/Album/10.jpg"> <Image.Effect> <hc:ColorComplementEffect/> </Image.Effect> </Image>- 该类在 ColorComplementEffect.cs 中只注册了
InputProperty,没有任何可配置项。 - 底层公式(ColorComplementEffect.fx):
float4 color = tex2D(implicitInput, uv); float4 complement; complement.rgb = color.a - color.rgb; // 以 alpha 为基准取补色 complement.a = color.a; return complement;对于完全不透明(alpha=1)的图片,等价于1 - RGB的标准反色/负片效果;对于半透明内容,则按当前像素 alpha 值计算补色,属于通用颜色数学意义上的“补色”(A 通道不受影响)。
ColorMatrixEffect 颜色矩阵特效
ColorMatrixEffect是最灵活也最强大的一款特效,通过一个 5 列 4 行的颜色矩阵对每个像素的 RGBA 进行线性变换,示例给出两种典型配置:
<!-- 配置一:反色风格 --> <Image Width="120" Height="120" Source="/HandyControlDemo;component/Resources/Img/Album/10.jpg"> <Image.Effect> <hc:ColorMatrixEffect M11="-1" M22="-1" M33="-1" M41="1" M42="1" M43="1" M44="1" M51="1"/> </Image.Effect> </Image> <!-- 配置二:三通道求和放大风格 --> <Image Width="120" Height="120" Source="/HandyControlDemo;component/Resources/Img/Album/10.jpg"> <Image.Effect> <hc:ColorMatrixEffect M11="1.5" M21="1.5" M31="1.5" M12="1.5" M22="1.5" M32="1.5" M13="1.5" M23="1.5" M33="1.5" M51="-1" M52="-1" M53="-1"/> </Image.Effect> </Image>20 个矩阵参数与默认值
该类在 ColorMatrixEffect.cs 中按“行(Line)”分四组注册了 20 个double依赖属性,分别映射到着色器常量寄存器 C0~C19:
| 寄存器 | 属性 | 默认值 | 寄存器 | 属性 | 默认值 |
|---|---|---|---|---|---|
| C0 | M11 | 1 | C10 | M13 | 0 |
| C1 | M21 | 0 | C11 | M23 | 0 |
| C2 | M31 | 0 | C12 | M33 | 1 |
| C3 | M41 | 0 | C13 | M43 | 0 |
| C4 | M51 | 0 | C14 | M53 | 0 |
| C5 | M12 | 0 | C15 | M14 | 0 |
| C6 | M22 | 1 | C16 | M24 | 0 |
| C7 | M32 | 0 | C17 | M34 | 0 |
| C8 | M42 | 0 | C18 | M44 | 1 |
| C9 | M52 | 0 | C19 | M54 | 0 |
默认状态下主对角线 M11 / M22 / M33 / M44 为 1、其余为 0,即恒等变换(不改变画面)。
矩阵语义与输出公式
着色器 ColorMatrixEffect.fx 的注释给出了完整定义:输出向量R = A·C,其中A为 4×5 矩阵、C为(R, G, B, A, 1)列向量。对应到属性,每个输出通道的计算式为:
R' = M11·R + M21·G + M31·B + M41·A + M51 G' = M12·R + M22·G + M32·B + M42·A + M52 B' = M13·R + M23·G + M33·B + M43·A + M53 A' = M14·R + M24·G + M34·B + M44·A + M54对照示例:
- 配置一(
M11=M22=M33=-1,M41=M42=M43=M44=1,M51=1):对不透明图像,G' = A - G、B' = A - B形成反色,R' = 1 + A - R在 alpha=1 时趋向输出白色(经 GPU 裁剪后红通道偏亮),整体呈现出以青色-品红为基调的负片风格化效果。 - 配置二(三行系数全为 1.5,
M51=M52=M53=-1):每个颜色通道都按1.5·(R+G+B) - 1计算,即三通道求和放大后再减去常数偏移,画面呈现高饱和、高亮的风格化渲染(中间灰会迅速逼近白色)。
由于 GPU 会对像素着色器输出自动裁剪到 [0,1],配置取值越界时不必担心崩溃,只需留意高亮区域会出现“溢出”带来的纯白块。矩阵特效非常适合实现反色、色相偏移、通道置换、饱和度/亮度调节等,属于可编程程度最高的一档特效。
ContrastEffect 对比度特效
ContrastEffect用于增强或削弱画面对比度,示例:
<Image Width="120" Height="120" Source="/HandyControlDemo;component/Resources/Img/Album/10.jpg"> <Image.Effect> <hc:ContrastEffect Contrast="20"/> </Image.Effect> </Image>- 参数:
Contrast(double),默认值1,注册见 ContrastEffect.cs。 - 取值影响:
Contrast = 1为原始画面;大于 1 提高对比度(亮者更亮、暗者更暗);小于 1(如0.5)降低对比度,画面向中性灰收敛。 - 底层公式(ContrastEffect.fx):
float4 color = tex2D(implicitInput, uv); float value = (1 - contrast) / 2; // 围绕中心灰的偏移 color.rgba = color.rgba * contrast + value; return color;从公式可见它是以中心灰(0.5 附近)为支点做线性拉伸:Contrast=20时,value = -9.5,color·20 - 9.5使画面两极分化极其强烈,接近海报化;反之低对比度则让所有像素向中性灰靠拢。
GrayScaleEffect 灰度特效
GrayScaleEffect按亮度平均法将画面转为灰度,并用Scale控制灰度与原始色的混合比例,示例:
<Image Width="120" Height="120" Source="/HandyControlDemo;component/Resources/Img/Album/10.jpg"> <Image.Effect> <hc:GrayScaleEffect Scale=".7"/> </Image.Effect> </Image>- 参数:
Scale(double),默认值1,注册见 GrayScaleEffect.cs。 - 取值影响:
Scale = 1为完全灰度;Scale = 0保持原色;0 < Scale < 1为灰度与原色之间的插值混合(示例0.7即 70% 灰度)。 - 底层公式(GrayScaleEffect.fx):
float4 color = tex2D(implicitInput, uv); float intensity = (color.r + color.g + color.b) / 3; // 通道均值作为亮度 complement.rgb = color.rgb * (1 - scale) + intensity * scale; complement.a = color.a; return complement;这里的灰度亮度采用简单的三通道算术平均(而非加权亮度公式),实现直观、开销低;alpha 通道始终原样保留,非常适合用来做“禁用态置灰”“鼠标悬停彩色/失焦灰度”之类的 UI 状态切换。
官方 Demo:效果组合与动画联动
所有特效的完整演示位于 EffectsDemo.xaml,该页面用UniformSpacingPanel平铺了 6 张相同图片,分别展示原始图、ColorComplementEffect、GrayScaleEffect(默认与Scale=".7")以及两组ColorMatrixEffect配置,可以直接对照运行效果验证参数:
- 第 2 张:
<hc:ColorComplementEffect/>,验证补色/负片效果; - 第 3 张:
<hc:GrayScaleEffect/>(默认Scale=1,完全灰度); - 第 4 张:
<hc:GrayScaleEffect Scale=".7"/>(灰度与原色 7:3 混合); - 第 5 张:反色风格的
ColorMatrixEffect配置一; - 第 6 张:三通道求和放大的
ColorMatrixEffect配置二。
页面底部则是BlendEffectBox的实战用法——把BlurEffect、BrightnessEffect、ContrastEffect三个特效同时作用在三个持续旋转(RepeatBehavior="Forever"的RotateTransform动画)的红色方块上,证明特效叠加与RenderTransform、动画等 WPF 渲染机制可以共存互不干扰。这也是官方推荐的组合范式:动画/变换交给常规 WPF 机制,画面风格化交给Effects。
小结
HandyControl 的这套特效组件全部基于ShaderEffect与打包的 PixelShader 实现,行为确定、性能高效,可直接以声明式 XAML 使用:
- 单特效:
BrightnessEffect(明度)、ColorComplementEffect(补色)、ColorMatrixEffect(颜色矩阵)、ContrastEffect(对比度)、GrayScaleEffect(灰度),挂在任意UIElement.Effect上即可; - 多特效:
BlendEffectBox以Effects集合 +ContentPresenter链式嵌套完成多特效叠加,无需手写嵌套容器; - 深度定制:如需扩展,参考 Resources/Effects 中的
.fx着色器源码与 Media/Effects 中的 C# 封装模式,编译.ps后替换PixelShader.UriSource即可接入自己的特效。
借助这套工具,你可以在不引入第三方图像库的前提下,为按钮、卡片、图片、水印等任意 WPF 元素快速实现明暗调节、负片、灰度禁用态、矩阵调色与多特效叠加等视觉能力。
- UI组件
- 桌面应用
【免费下载链接】HandyControl
Contains some simple and commonly used WPF controls
相关推荐
HandyControl ColorPicker 颜色拾取器:仿 Chrome 的 WPF 颜色选择控件实战解析
HandyControl ColorPicker 颜色拾取器:仿 Chrome 的 WPF 颜色选择控件实战解析 ColorPicker 是 HandyCont
UI组件桌面应用终极Isomer颜色系统解析:轻松实现HTML5 Canvas透明度与混合效果
终极Isomer颜色系统解析:轻松实现HTML5 Canvas透明度与混合效果 Isomer是一款轻量级的HTML5 Canvas等距图形库,它提供了强大的颜色
图形学TiXL 图像着色算子 Tint 完全指南:明暗双色映射、灰度转换与阈值效果实战
TiXL 图像着色算子 Tint 完全指南:明暗双色映射、灰度转换与阈值效果实战 本文以 TiXL 开源实时动态图形软件(仓库根目录 t3 )中 Lib.ima
音视频图形学桌面应用
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考