1. 程式人生 > >分享一個算法,計算能在任何背景色上清晰顯示的前景色

分享一個算法,計算能在任何背景色上清晰顯示的前景色

gray marker link media cef edit ref 相關 spa

原文:分享一個算法,計算能在任何背景色上清晰顯示的前景色

版權聲明:本作品采用知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議進行許可。歡迎轉載、使用、重新發布,但務必保留文章署名呂毅(包含鏈接:http://blog.csdn.net/wpwalter/),不得用於商業目的,基於本文修改後的作品務必以相同的許可發布。如有任何疑問,請與我聯系([email protected])。 https://blog.csdn.net/WPwalter/article/details/78671680

背景色千差萬別,如果希望在這樣復雜的背景色下顯示清晰可辨的前景色(例如顯示文字),那如何選擇這樣的前景色才能確保適用於所有的背景呢?


灰度圖的心理學公式

紅綠藍三色是非常不直觀的顏色表示的方法,如果不經過訓練,人類幾乎沒有辦法直接通過 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 即可。

技術分享圖片
技術分享圖片
技術分享圖片

我封裝的方便的 API

不過,總是寫後臺代碼來計算,對於 XAML 類的程序來說還是麻煩了些,於是我寫了一些用於 XAML 的標記擴展,方便讓一些文字自動根據背景色改變顏色。

這是期望的最簡用法:

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

因為內部已經使用綁定來實現動態變化,所以,無需在顏色更改時再次更新:

技術分享圖片

由於這份封裝的 API 目前還在完善中,會經常改動,所以只貼出 GitHub 倉庫地址,不放在這裏:

  • LuminanceForegroundExtension 寫出此用法的關鍵類
  • LuminanceReverseColor 包含亮度灰度值反色的邏輯
  • DependencyMarkupExtension 給標記擴展中一些惡心的代碼提供封裝

參考資料

  • Luma (video) - Wikipedia
  • 從RGB色轉為灰度色算法(轉) - carekee - 博客園

分享一個算法,計算能在任何背景色上清晰顯示的前景色