在Flutter中将图像转换为灰度:ColorFiltered组件

背景

在最近的项目中,我遇到了一个需求:当满足特定条件时,需要将图像转换为灰度。例如,用户个人资料页面有一组用来显示个人社交信息,并展示相关社交平台图标,如果用设置了社交信息,则该社交平台的图标应处理点亮状态,如果没有设置,则应处于禁用状态,也就是是说,我需要把对应图标的颜色改为灰度模式。这看起来很简单,特别是对于那些单一颜色的图标,对吧?但实际上,有一些图标是彩色的而且色彩丰富,比如说instagram。

BlendMode

我首先研究了Flutter中的混合模式(BlendMode),结果发现它们确实效果很好 ------ 但这仅适用于没有透明度的JPEGPNG 图像。以下是我用于将彩色透明PNG图像显示为灰度图像的代码。

dart 复制代码
ColorFiltered(
        colorFilter: enabled ? ColorFilter.mode(
          Colors.transparent,
          BlendMode.multiply,
        ) : ColorFilter.mode(
          Colors.grey,
          BlendMode.saturation,
        ),
        child: Image.asset("assets/roundabout.png",),
      )

上面用的图片是一个带有透明的PNG图片,让我们看看效果:

再放一个没有经过处理的demo用的原图:

很显然,这样的效果很糟糕。混合模式(BlendMode)也会影响图像中的透明部分,并将它们也转换为灰色,但我们需要让这些透明区域也不受影响。

颜色矩阵

经过一番研究,我在Flutter文档的深处发现了这个内容。文档中提到了另一个构造函数,它可以用来定义一个矩阵,从而更精细地控制混合模式的应用方式。

于是,我用ColorFilter.matrix替换了ColorFilter.mode构造函数,并传入了这些神奇的数字。

dart 复制代码
ColorFilter.matrix(<double>[
 0.2126,0.7152,0.0722,0,0,
 0.2126,0.7152,0.0722,0,0,
 0.2126,0.7152,0.0722,0,0,
 0,0,0,1,0,
])

没错,这正是我一直在寻找的解决方案。文档中还提到了更多示例。大家可以随意尝试这些示例,并对这些数值进行调整。

** 反色(Inversion)** 效果的颜色矩阵:

dart 复制代码
const ColorFilter invert = ColorFilter.matrix(<double>[
   -1, 0, 0, 0, 255,
   0, -1, 0, 0, 255,
   0, 0, -1, 0, 255,
   0, 0, 0, 1, 0, ]);

** 复古棕褐色(Sepia Tone)** 效果的颜色矩阵

dart 复制代码
const ColorFilter sepia = ColorFilter.matrix(<double>[ 
   0.393, 0.769, 0.189, 0, 0, 
   0.349, 0.686, 0.168, 0, 0,
   0.272, 0.534, 0.131, 0, 0, 
   0, 0, 0, 1, 0, ]);

个人感觉这个效果适用于复古风格 UI、老照片效果、特定主题界面

灰度滤镜(Grayscale Color Filter) 效果矩阵

标准灰度滤镜矩阵(最常用)

dart 复制代码
const ColorFilter greyscale = ColorFilter.matrix(<double>[ 
  0.2126, 0.7152, 0.0722, 0, 0, // 红色通道转换
  0.2126, 0.7152, 0.0722, 0, 0, // 绿色通道转换
  0.2126, 0.7152, 0.0722, 0, 0, // 蓝色通道转换
  0, 0, 0, 1, 0, //透明度保持不变
  ]);

上面的的灰度算法是一个标准的灰度算法,还两种算法可供参考:

简化版灰度矩阵(平均法)

dart 复制代码
ColorFilter.matrix([ 
0.333, 0.333, 0.333, 0, 0, // 红色通道取平均值 
0.333, 0.333, 0.333, 0, 0, // 绿色通道取平均值 
0.333, 0.333, 0.333, 0, 0, // 蓝色通道取平均值 
0, 0, 0, 1, 0, // 透明度保持不变 
])

高对比度灰度矩阵

dart 复制代码
ColorFilter.matrix([ 
0.299, 0.587, 0.114, 0, 0, // 红色通道(电视标准算法) 
0.299, 0.587, 0.114, 0, 0, // 绿色通道 
0.299, 0.587, 0.114, 0, 0, // 蓝色通道 
0, 0, 0, 1, 0, // 透明度保持不变
])

矩阵原理

  • 人眼对红、绿、蓝三色的敏感度不同,公式 亮度 = 0.2126*R + 0.7152*G + 0.0722*B 可模拟真实灰度效果。
  • 矩阵中每行对应一个颜色通道(红、绿、蓝、透明度),每列对应输入颜色的分量(R、G、B、A、偏移量)。

让我们再回头看看BlendMode相关属性:

关键属性解析

属性 说明
colorFilter 必需参数,指定颜色滤镜。可通过 ColorFilter.modeColorFilter.matrix 创建。
BlendMode 混合模式(如 colorBurnsrcIn 等),影响颜色过滤的最终效果。
child 待处理的子组件(通常为 ImageIcon 等可视化组件)。

注意事项

  1. 性能影响

    • 复杂的 ColorFilter.matrix 可能对性能产生轻微影响,建议对静态图像提前处理为灰度图。
    • 动态切换时,避免在列表或高频率刷新场景中大量使用。
  2. 混合模式选择

    • BlendMode.color 可保留亮度,仅修改色相和饱和度,适合部分灰度效果。
    • BlendMode.luminosity 直接提取亮度,是最纯净的灰度转换模式。

最后

通过 ColorFiltered 组件,Flutter 提供了简洁高效的图像灰度处理方案。无论是静态效果还是动态交互,合理选择混合模式或矩阵运算,均可灵活实现所需视觉效果。

也希望大家关注我的微信公众号OpenFlutter

相关推荐
拉不动的猪33 分钟前
都25年啦,还有谁分不清双向绑定原理,响应式原理、v-model实现原理
前端·javascript·vue.js
烛阴1 小时前
Python枚举类Enum超详细入门与进阶全攻略
前端·python
孟孟~1 小时前
npm run dev 报错:Error: error:0308010C:digital envelope routines::unsupported
前端·npm·node.js
孟孟~1 小时前
npm install 报错:npm error: ...node_modules\deasync npm error command failed
前端·npm·node.js
狂炫一碗大米饭1 小时前
一文打通TypeScript 泛型
前端·javascript·typescript
wh_xia_jun1 小时前
在 Spring Boot 中使用 JSP
java·前端·spring boot
二十雨辰1 小时前
[HTML5]快速掌握canvas
前端·html
tingkeiii2 小时前
【react+antd+vite】优雅的引入svg和阿里巴巴图标
前端·react.js·前端框架
清幽竹客2 小时前
vue-18(使用 Vuex 插件实现高级功能)
前端·vue.js·前端框架·vue
粥里有勺糖2 小时前
用Trae做了个公众号小工具
前端·ai编程·trae