WPF 使用GDI+提取图片主色调并生成Mica材质特效背景


 

先看效果,在浅色模式下:

在深色模式下:

P.S. 此算法只是尽可能地接近Windows Mica效果,并非实际实现;主色调提取算法只能确保在绝大多数情况下适用。

测试项目在Github上开源:

TwilightLemon/MicaImageTest: WPF 使用GDI+提取图片主色调并生成Mica材质特效背景

一、简要原理和设计

1.1 Mica效果

Mica效果是Windows 11的一个新特性,旨在为应用程序提供一种更柔和的背景效果。它通过使用桌面壁纸的颜色和纹理来创建一个静态的模糊背景效果。一个大致的模拟过程如下:

  1. 根据颜色模式(浅色或深色)来调整图像对比度
  2. 增加一个白色/黑色的遮罩层
  3. 大半径 高斯模糊处理

在仓库代码中给出了所有组件的实现,如果你想调整效果,可以修改以下几个值:

复制代码
1 public static void ApplyMicaEffect(this Bitmap bitmap,bool isDarkmode)
2 {
3     bitmap.AdjustContrast(isDarkmode?-1:-20);//Light Mode通常需要一个更高的对比度
4     bitmap.AddMask(isDarkmode);//添加遮罩层
5     bitmap.ScaleImage(2);//放大图像(原始图像一般为500x500)以提高输出图像质量
6     var rect = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
7     bitmap.GaussianBlur(ref rect, 80f, false);//按需要调整模糊半径
8 }
复制代码

1.2 主色调提取与微调

从原始图像中提取主色调,主要过程如下:

  1. 像素采样和颜色量化便于统计
  2. 过滤过黑或过白的颜色值(我们会在调整步骤单独处理)
  3. 根据HSL的饱和度和亮度来计算权重,
    • 饱和度越高,权重越大
    • 亮度稳定(我们定为0.6),权重越大
  4. 选择权重最大的颜色均值作为主色调

之后为了适配UI,保证亮度、饱和度适合用于呈现内容,还要对颜色进行微调:

  1. 将颜色转为HSL空间
  2. 根据颜色模式调节亮度
  3. 分层调整饱和度,一般来说暗色模式的对比度比亮色模式高
  4. 对特定色相区间(红/绿/蓝/黄)进行差异化调整

最后计算焦点颜色(FocusAccentColor)只需要根据颜色模式调整亮度即可。

二、使用方法

将代码仓库中的ImageHelper.cs添加到项目,然后在需要的地方调用Bitmap的扩展方法来处理图像。以下是一个简单的示例:

首先开启项目允许使用UnSafe代码:

  <PropertyGroup>
    <!-- 允许使用UnSafe代码 -->
    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
  </PropertyGroup>  

导入本地图像文件,计算主色调、焦点色调并应用Mica效果背景:

复制代码
 var image=new BitmapImage(new Uri(ImagePath));
 SelectedImg = image;
 var bitmap = image.ToBitmap();
 //major color
 var majorColor = bitmap.GetMajorColor().AdjustColor(IsDarkMode);
 var focusColor = majorColor.ApplyColorMode(IsDarkMode);
 App.Current.Resources["AccentColor"] = new SolidColorBrush(majorColor);
 App.Current.Resources["FocusedAccentColor"] = new SolidColorBrush(focusColor);
 //background
 bitmap.ApplyMicaEffect(IsDarkMode);
 BackgroundImg = bitmap.ToBitmapImage();
复制代码

其中,SelectedImgBackgroundImg是绑定到UI的BitmapImage类型属性,IsDarkMode是指示当前颜色模式的布尔值。

三、注意事项

  1. 处理大图像时可能会导致性能下降,建议使用较小的图像或在后台线程中处理。
  2. 如果高斯模糊组件报错,请确保Nuget包System.Drawing.Common的版本为8.0.1,因为代码中使用了反射获取Bitmap内部的句柄。
  3. 你可能需要根据实际情况调整模糊半径和对比度等参数,以获得最佳效果。
  4. 库中实现可能并非最佳写法,如果有更好的方法可以提交PR或者评论区见。

最后附上ImageHelper.cs的完整代码:

 View Code

 

  本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名TwilightLemon,不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。

posted on 2025-05-29 10:30  漫思  阅读(53)  评论(0)    收藏  举报

导航