如何检查背景颜色的亮度以决定文本颜色?

3

我有一个简单的问题,但是我找不到答案。 请注意,我几乎是个完全的新手。

所以我有一个应用程序(不是我的,但我正在为它做贡献),里面有文字和彩色背景,用户可以更改背景颜色。 如果背景足够亮,则文字应该显示为黑色,但如果背景不够亮,则应该显示为白色。

该应用程序是一款针对匈牙利小学和中学生的校园日记应用程序,连接到全国范围的校园日记服务。在此,最好的记录是5分,最差的是1分。用户可以在设置中设置每个等级的颜色。目前,只有笔记“4”的代码硬编码为具有黑色文本(因为默认情况下“4”笔记的背景色为黄色),所有其他文本都是白色的。这就是我想要自动化的。

白色文本的示例

黑色文本的示例

这是参考的应用程序主屏幕

用户可以更改笔记类型颜色的页面

目前的代码:

switch (evaluation.NumberValue) {
    case 1:
      bColor = globals.color1;
      fColor = Colors.white;
      break;
    case 2:
      bColor = globals.color2;
      fColor = Colors.white;
      break;
    case 3:
      bColor = globals.color3;
      fColor = Colors.white;
      break;
    case 4:
      bColor = globals.color4;
      fColor = Colors.black; //This should be white if color4 is dark enough. Same applies to all of them.
      break;
    case 5: //I'm looking for something like this:
      bColor = globals.color5;
      fColor = (lightLevel(globals.color5) > 50) ? Colors.black : Colors.white;
      break;
    default:
      bColor = Colors.black;
      fColor = Colors.white;
      break;
  }

我正在寻找类似以下内容的东西:
 case 5: //I'm looking for something like this:
  bColor = globals.color5;
  fColor = (lightLevel(globals.color5) > 50) ? Colors.black : Colors.white;
  break;

感谢任何帮助!
3个回答

10
我可以建议两种选项:
  1. ThemeData.estimateBrightnessForColor方法,例如。

此方法可用于确定给定颜色的亮度主题。
Color calculateTextColor(Color background) {
  return ThemeData.estimateBrightnessForColor(background) == Brightness.light ? Colors.black : Colors.white;
}

  1. Color.computeLuminance 方法,例如:
Color calculateTextColor(Color background) {
  return background.computeLuminance() >= 0.5 ? Colors.black : Colors.white;
}

请记住,这两个方法都计算起来代价高昂。前者在幕后使用了后者。区别在于如何计算光与暗之间的阈值。


非常感谢!第一个无法工作,但如果该方法速度慢,那没问题,因为它只被调用一次,当用户更改颜色时。 - RedyAu
第一个也很昂贵,因为它在底层使用了 computeLuminance。记住这一点。 - PrzemekTom
@Przemo 很好的观点。感谢您对答案的贡献! - George Zvonov

2
Brightness.estimateBrightnessForColor method e.g.
Color calculateTextColor(Color background) {
  return Brightness.estimateBrightnessForColor(background) == Brightness.light ? Colors.black : Colors.white;
}
我发现方法estimateBrightnessForColor现在是ThemeData类的一部分,所以现在需要这样调用:
ThemeData.estimateBrightnessForColor(background)

1
谢谢,我已经相应地更新了正确答案的标记 :) - RedyAu
我已经将被接受的答案改回来了,因为这也使用了昂贵的计算。 - RedyAu

1

简短回答

我在这里GitHub上写过相关内容,并附带了一些互动实验。

这个问题被经常提问,所以我最近创建了一个名为Max Contrast的简洁高效的字体颜色翻转工具的仓库。

更详细的回答

为了概括之前的回答:

1. 要进行阅读,需要亮度对比度,因此设计中使用的颜色必须转换为使用红、绿、蓝显示器原色的加权值来实现无色亮度。 2. 使用感知统一对比度数学方法(如APCA),您可以确定白色和黑色之间的文本对比度中心点。 - 实际上,该中心是一个小范围,在使用环境、环境和其他因素的情况下,黑色或白色可能具有优势。 3. 使用标准亮度计算,SDR sRGB显示器的理想中心约为36 Y。范围约为34Y至42Y,具体取决于各种因素。 4. 中心区域的另一个因素是需要比较高对比度的外部区域使用较大的字体。范围约为22Y至53Y,如果已经使用了大字体,则可以忽略不计。这在"Flip for Color"演示页面上有所展示。 5. 来自Max Contrast的关键片段。
    // Simple: send it sRGB values 0-255.
   // If Ys is > 0.342 it returns 'black' otherwise returns 'white'
  // See  https://github.com/Myndex/max-contrast 
 // trc = gamma, Rco = red coefficient, Rs = Red signal
// Ys = estimated screen luminance (APCA 0.0.98G4g)

function maxContrast (Rs = 164, Gs = 164, Bs = 164) {
  
  const flipYs = 0.342; // based on APCA™ 0.98G middle contrast BG
  const trc = 2.4, Rco = 0.2126729, Gco = 0.7151522, Bco = 0.0721750; // 0.98G
  let Ys = (Rs/255.0)**trc*Rco + (Gs/255.0)**trc*Gco + (Bs/255.0)**trc*Bco; 

  return Ys < flipYs ? 'white' : 'black'
}

注意:此代码片段使用APCA兼容的常量。翻转值设置为YS 34.2,这是APCA估计的屏幕亮度,针对自发光显示器进行了优化,并且与CIE亮度(Y)计算略有不同。需要较大字体的“字体范围”为YS 20至YS 50。

1
非常感谢您提供这个技术性的答案,太棒了! - undefined
我知道代码中的“Rco”代表“红色”。但是“Rs”,“Bs”,“Gs”,“trc”和“Ys”代表什么呢? - undefined
1
嗨 @SeptianDika ... 啊哦,抱歉,Rco 不是指红色,而是指红色系数。我可能需要澄清这些事情。它是用于调整红色信号相对亮度贡献的常数。Rs 指的是来自 sRGB 的红色,trc 意味着传输曲线色调响应曲线,而 Ys 意味着估计的屏幕亮度。在 https://readtech.org/ARC/ 上有一个术语表。这还在不断完善中... - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接