当前位置: 首页 > news >正文

分享一个算法,计算能在任何背景色上清晰显示的前景色

背景色千差万别,如果希望在这样复杂的背景色下显示清晰可辨的前景色(例如显示文字),那如何选择这样的前景色才能确保适用于所有的背景呢?


灰度图的心理学公式

红绿蓝三色是非常不直观的颜色表示的方法,如果不经过训练,人类几乎没有办法直接通过 RGB 的值来猜出大概的颜色来。而 HSB 是用来解决人眼感知问题的,它将颜色用色相、饱和度、明度来表示。

可是,即便是 HSB 也不能完美解决人眼的感知问题。看下图,黄色和蓝色的饱和度和明度一样,只是色相不同,你觉得哪一个颜色更亮,哪一个更暗?

黄色和蓝色的感知亮度对比

相信大家都会觉得黄色更亮,蓝色总给人一种阴暗的感觉。

所以,在饱和度和明度之外,一定还有一种人眼对亮度的感觉是与色相相关的。

我们将不同色相的颜色排成一圈,观察下哪些颜色更亮,哪些更暗:

相同饱和度明度下的不同色相

我们将上面的不同颜色直接转成灰度图像,这是最能反映人眼感知的灰度图像,它将是这样的:

不同色相给人的心里感知亮度

也就是说,不同的颜色值总能找到一个人眼感知的灰度值,这是著名的心理学公式:

灰度 = 红×0.299 + 绿×0.587 + 蓝×0.114

在灰度背景色上决定前景色

一个图像的每一个像素经过上面的公式计算得到的新的图像,即是人眼感知亮度的灰度图。

于是,当我们期望计算一个能在背景色上清晰显示的前景色时,我们可将背景颜色转换为灰度颜色,然后根据灰度程度,选取黑色或白色作为前景色。

当然,如果你喜欢,可以将一段黑色或接近于黑色的灰度色作为浅色背景的前景;将一段白色或颉俊宇白色的灰度色作为深色背景的前景。

代码实现

为了实现这个效果,我们先写一个灰度/亮度的计算函数:

/// <summary>
/// 获取一个颜色的人眼感知亮度,并以 0~1 之间的小数表示。
/// </summary>
private static double GetGrayLevel(Color color)
{
    return (0.299 * color.R + 0.587 * color.G + 0.114 * color.B) / 255;
}

然后写一个根据感知亮度计算反色的方法:

private static Color GetReverseForegroundColor(double grayLevel) => grayLevel > 0.5 ? Colors.Black : Colors.White;

于是,当我们希望计算某个背景色上一定能清晰显示的前景色时,只需要调用 GetReverseForegroundColor 即可。

测试 ForestGreen 颜色
测试 Teal 颜色
测试 YellowGreen 颜色

我封装的方便的 API

不过,总是写后台代码来计算,对于 XAML 类的程序来说还是麻烦了些,于是我写了一些用于 XAML 的标记扩展,方便让一些文字自动根据背景色改变颜色。

这是期望的最简用法:

<TextBlock Foreground="{media:LuminancedForeground}" Text="我是前景 by walterlv"/>

因为内部已经使用绑定来实现动态变化,所以,无需在颜色更改时再次更新:

支持动态的背景色

由于这份封装的 API 目前还在完善中,会经常改动,所以只贴出 GitHub 仓库地址,不放在这里:

  • LuminanceForegroundExtension 写出此用法的关键类
  • LuminanceReverseColor 包含亮度灰度值反色的逻辑
  • DependencyMarkupExtension 给标记扩展中一些恶心的代码提供封装

参考资料

  • Luma (video) - Wikipedia
  • 从RGB色转为灰度色算法(转) - carekee - 博客园

相关文章:

  • WPF 绘制对齐像素的清晰显示的线条
  • 让 ScrollViewer 的滚动带上动画
  • UI 设计中的视觉无障碍设计
  • 为什么委托的减法(- 或 -=)可能出现非预期的结果?(Delegate Subtraction Has Unpredictable Result)
  • 将美化进行到底,使用 Oh My Posh 把 PowerShell 做成 oh-my-zsh 的样子
  • 实现一个 WPF 版本的 ConnectedAnimation
  • C#/.NET 中的契约
  • WPF 自定义键盘焦点样式(FocusVisualStyle)
  • 异步任务中的重新进入(Reentrancy)
  • 迫不及待地体验了一把 C#8.0 中的可空引用类型(Nullable Reference)
  • C#/.NET 匿名函数会捕获变量,并延长对象的生命周期
  • 卡诺模型(KANO Model)
  • .NET 中的轻量级线程安全
  • 将 WPF、UWP 以及其他各种类型的旧样式的 csproj 文件迁移成新样式的 csproj 文件
  • .NET Core 和 .NET Framework 中的 MEF2
  • [PHP内核探索]PHP中的哈希表
  • ➹使用webpack配置多页面应用(MPA)
  • express + mock 让前后台并行开发
  • Hibernate最全面试题
  • Java,console输出实时的转向GUI textbox
  • Linux各目录及每个目录的详细介绍
  • linux学习笔记
  • MYSQL 的 IF 函数
  • nodejs实现webservice问题总结
  • react-core-image-upload 一款轻量级图片上传裁剪插件
  • Vim 折腾记
  • vue从创建到完整的饿了么(11)组件的使用(svg图标及watch的简单使用)
  • vue总结
  • webpack4 一点通
  • 前端工程化(Gulp、Webpack)-webpack
  • 算法系列——算法入门之递归分而治之思想的实现
  • 新书推荐|Windows黑客编程技术详解
  • 用jQuery怎么做到前后端分离
  • 微龛半导体获数千万Pre-A轮融资,投资方为国中创投 ...
  • ​ 无限可能性的探索:Amazon Lightsail轻量应用服务器引领数字化时代创新发展
  • ​​​​​​​GitLab 之 GitLab-Runner 安装,配置与问题汇总
  • ​​​​​​​sokit v1.3抓手机应用socket数据包: Socket是传输控制层协议,WebSocket是应用层协议。
  • ​3ds Max插件CG MAGIC图形板块为您提升线条效率!
  • ​渐进式Web应用PWA的未来
  • ​一文看懂数据清洗:缺失值、异常值和重复值的处理
  • ## 临床数据 两两比较 加显著性boxplot加显著性
  • #git 撤消对文件的更改
  • (2)关于RabbitMq 的 Topic Exchange 主题交换机
  • (3)选择元素——(14)接触DOM元素(Accessing DOM elements)
  • (二)构建dubbo分布式平台-平台功能导图
  • (附源码)ssm经济信息门户网站 毕业设计 141634
  • (七)Java对象在Hibernate持久化层的状态
  • (收藏)Git和Repo扫盲——如何取得Android源代码
  • (转)为C# Windows服务添加安装程序
  • ./configure,make,make install的作用(转)
  • .NET “底层”异步编程模式——异步编程模型(Asynchronous Programming Model,APM)...
  • .NET 5.0正式发布,有什么功能特性(翻译)
  • .net 8 发布了,试下微软最近强推的MAUI
  • .Net Winform开发笔记(一)
  • .NET 常见的偏门问题