summaryrefslogtreecommitdiff
path: root/lisp/faces.el
diff options
context:
space:
mode:
Diffstat (limited to 'lisp/faces.el')
-rw-r--r--lisp/faces.el50
1 files changed, 38 insertions, 12 deletions
diff --git a/lisp/faces.el b/lisp/faces.el
index 4d1d9561d49..ba85973bf10 100644
--- a/lisp/faces.el
+++ b/lisp/faces.el
@@ -1560,7 +1560,7 @@ is given, in which case return its value instead."
;; return it to the caller. Since there will most definitely be something to
;; return in this case, there's no need to know/check if a match was found.
(if defaults
- (append result defaults)
+ (append defaults result)
(if match-found
result
no-match-retval))))
@@ -1785,16 +1785,42 @@ with the color they represent as background color."
(defined-colors frame)))
(defun readable-foreground-color (color)
- "Return a readable foreground color for background COLOR."
- (let* ((rgb (color-values color))
- (max (apply #'max rgb))
- (black (car (color-values "black")))
- (white (car (color-values "white"))))
- ;; Select black or white depending on which one is less similar to
- ;; the brightest component.
- (if (> (abs (- max black)) (abs (- max white)))
- "black"
- "white")))
+ "Return a readable foreground color for background COLOR.
+The returned value is a string representing black or white, depending
+on which one provides better contrast with COLOR."
+ ;; We use #ffffff instead of "white", because the latter is sometimes
+ ;; less than white. That way, we get the best contrast possible.
+ (if (color-dark-p (mapcar (lambda (c) (/ c 65535.0))
+ (color-values color)))
+ "#ffffff" "black"))
+
+(defconst color-luminance-dark-limit 0.325
+ "The relative luminance below which a color is considered 'dark'.
+A 'dark' color in this sense provides better contrast with white
+than with black; see `color-dark-p'.
+This value was determined experimentally.")
+
+(defun color-dark-p (rgb)
+ "Whether RGB is more readable against white than black.
+RGB is a 3-element list (R G B), each component in the range [0,1].
+This predicate can be used both for determining a suitable (black or white)
+contrast colour with RGB as background and as foreground."
+ (unless (<= 0 (apply #'min rgb) (apply #'max rgb) 1)
+ (error "RGB components %S not in [0,1]" rgb))
+ ;; Compute the relative luminance after gamma-correcting (assuming sRGB),
+ ;; and compare to a cut-off value determined experimentally.
+ ;; See https://en.wikipedia.org/wiki/Relative_luminance for details.
+ (let* ((sr (nth 0 rgb))
+ (sg (nth 1 rgb))
+ (sb (nth 2 rgb))
+ ;; Gamma-correct the RGB components to linear values.
+ ;; Use the power 2.2 as an approximation to sRGB gamma;
+ ;; it should be good enough for the purpose of this function.
+ (r (expt sr 2.2))
+ (g (expt sg 2.2))
+ (b (expt sb 2.2))
+ (y (+ (* r 0.2126) (* g 0.7152) (* b 0.0722))))
+ (< y color-luminance-dark-limit)))
(declare-function xw-color-defined-p "xfns.c" (color &optional frame))
@@ -1822,7 +1848,7 @@ COLOR should be a string naming a color (e.g. \"white\"), or a
string specifying a color's RGB components (e.g. \"#ff12ec\").
Return a list of three integers, (RED GREEN BLUE), each between 0
-and either 65280 or 65535 (the maximum depends on the system).
+and 65535 inclusive.
Use `color-name-to-rgb' if you want RGB floating-point values
normalized to 1.0.