(downcase b))))))
(setq color (list color)))
(let* ((opoint (point))
- (color-values (color-values (car color)))
- (light-p (>= (apply 'max color-values)
- (* (car (color-values "white")) .5))))
+ (fg (readable-foreground-color (car color))))
(insert (car color))
(indent-to 22)
- (put-text-property opoint (point) 'face `(:background ,(car color)))
+ (put-text-property opoint (point) 'face `(:background ,(car color)
+ :foreground ,fg))
(put-text-property
(prog1 (point)
(insert " ")
(insert (propertize
(apply 'format "#%02x%02x%02x"
(mapcar (lambda (c) (ash c -8))
- color-values))
+ (color-values (car color))))
'mouse-face 'highlight
'help-echo
(let ((hsv (apply 'color-rgb-to-hsv
opoint (point)
'follow-link t
'mouse-face (list :background (car color)
- :foreground (if light-p "black" "white"))
+ :foreground fg)
'color-name (car color)
'action callback-fn)))
(insert "\n"))
(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))
+ "#ffffff" "black"))
+
+(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 (eval-when-compile (expt 0.6 2.2)))))
(declare-function xw-color-defined-p "xfns.c" (color &optional frame))
;; Evaluate to the color if the name is found.
((css--named-color start-point match))))
-(defun css--contrasty-color (name)
- "Return a color that contrasts with NAME.
-NAME is of any form accepted by `color-distance'.
-The returned color will be usable by Emacs and will contrast
-with NAME; in particular so that if NAME is used as a background
-color, the returned color can be used as the foreground and still
-be readable."
- ;; See bug#25525 for a discussion of this.
- (if (> (color-distance name "black") 292485)
- "black" "white"))
-
(defcustom css-fontify-colors t
"Whether CSS colors should be fontified using the color as the background.
When non-`nil', a text representing CSS color will be fontified
(add-text-properties
start (point)
(list 'face (list :background color
- :foreground (css--contrasty-color color)
+ :foreground (readable-foreground-color
+ color)
:box '(:line-width -1))))))))))))
extended-region))