diff options
| author | Julien Danjou | 2010-11-23 00:03:44 +0000 |
|---|---|---|
| committer | Katsumi Yamaoka | 2010-11-23 00:03:44 +0000 |
| commit | ef6a29070d822e6b35d6b978d2f070f8a5854b30 (patch) | |
| tree | abd8284262583146aae1a714435580d32e749b57 | |
| parent | 8a0eb8520219bea72cac5410684b0a0ca9d3c783 (diff) | |
| download | emacs-ef6a29070d822e6b35d6b978d2f070f8a5854b30.tar.gz emacs-ef6a29070d822e6b35d6b978d2f070f8a5854b30.zip | |
shr.el (shr-tag-color-check): Convert colors to hexadecimal with shr-color->hexadecimal.
shr-color.el (shr-color->hexadecimal): Add converting functions for RGB() or HSL() color representation.
shr.el (shr-tag-font): Add.
(shr-tag-color-check): New function to get better colors.
(shr-tag-insert-color-overlay): Factorize code between tag-font and tag-span.
shr-color.el: New file.
color-lab.el: New file.
| -rw-r--r-- | lisp/gnus/ChangeLog | 15 | ||||
| -rw-r--r-- | lisp/gnus/color-lab.el | 241 | ||||
| -rw-r--r-- | lisp/gnus/shr-color.el | 179 | ||||
| -rw-r--r-- | lisp/gnus/shr.el | 25 |
4 files changed, 460 insertions, 0 deletions
diff --git a/lisp/gnus/ChangeLog b/lisp/gnus/ChangeLog index 14eaec1052b..d791cf10aaf 100644 --- a/lisp/gnus/ChangeLog +++ b/lisp/gnus/ChangeLog | |||
| @@ -1,5 +1,20 @@ | |||
| 1 | 2010-11-22 Julien Danjou <julien@danjou.info> | 1 | 2010-11-22 Julien Danjou <julien@danjou.info> |
| 2 | 2 | ||
| 3 | * shr.el (shr-tag-color-check): Convert colors to hexadecimal with | ||
| 4 | shr-color->hexadecimal. | ||
| 5 | |||
| 6 | * shr-color.el (shr-color->hexadecimal): Add converting functions for | ||
| 7 | RGB() or HSL() color representation. | ||
| 8 | |||
| 9 | * shr.el (shr-tag-font): Add. | ||
| 10 | (shr-tag-color-check): New function to get better colors. | ||
| 11 | (shr-tag-insert-color-overlay): Factorize code between tag-font and | ||
| 12 | tag-span. | ||
| 13 | |||
| 14 | * shr-color.el: New file. | ||
| 15 | |||
| 16 | * color-lab.el: New file. | ||
| 17 | |||
| 3 | * gnus-art.el (gnus-url-mailto): Do not downcase args. | 18 | * gnus-art.el (gnus-url-mailto): Do not downcase args. |
| 4 | 19 | ||
| 5 | 2010-11-21 Andrew Cohen <cohen@andy.bu.edu> | 20 | 2010-11-21 Andrew Cohen <cohen@andy.bu.edu> |
diff --git a/lisp/gnus/color-lab.el b/lisp/gnus/color-lab.el new file mode 100644 index 00000000000..997bdbfc145 --- /dev/null +++ b/lisp/gnus/color-lab.el | |||
| @@ -0,0 +1,241 @@ | |||
| 1 | ;;; color-lab.el --- Color manipulation laboratory routines | ||
| 2 | |||
| 3 | ;; Copyright (C) 2010 Free Software Foundation, Inc. | ||
| 4 | |||
| 5 | ;; Author: Julien Danjou <julien@danjou.info> | ||
| 6 | ;; Keywords: html | ||
| 7 | |||
| 8 | ;; This file is part of GNU Emacs. | ||
| 9 | |||
| 10 | ;; GNU Emacs is free software: you can redistribute it and/or modify | ||
| 11 | ;; it under the terms of the GNU General Public License as published by | ||
| 12 | ;; the Free Software Foundation, either version 3 of the License, or | ||
| 13 | ;; (at your option) any later version. | ||
| 14 | |||
| 15 | ;; GNU Emacs is distributed in the hope that it will be useful, | ||
| 16 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 17 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 18 | ;; GNU General Public License for more details. | ||
| 19 | |||
| 20 | ;; You should have received a copy of the GNU General Public License | ||
| 21 | ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. | ||
| 22 | |||
| 23 | ;;; Commentary: | ||
| 24 | |||
| 25 | ;; This package provides color manipulation functions. | ||
| 26 | |||
| 27 | ;;; Code: | ||
| 28 | |||
| 29 | (defun rgb->hsv (red green blue) | ||
| 30 | "Convert RED GREEN BLUE values to HSV representation. | ||
| 31 | Hue is in radian. Saturation and values are between 0 and 1." | ||
| 32 | (let* ((r (float red)) | ||
| 33 | (g (float green)) | ||
| 34 | (b (float blue)) | ||
| 35 | (max (max r g b)) | ||
| 36 | (min (min r g b))) | ||
| 37 | (list | ||
| 38 | (/ (* 2 float-pi | ||
| 39 | (cond ((and (= r g) (= g b)) 0) | ||
| 40 | ((and (= r max) | ||
| 41 | (>= g b)) | ||
| 42 | (* 60 (/ (- g b) (- max min)))) | ||
| 43 | ((and (= r max) | ||
| 44 | (< g b)) | ||
| 45 | (+ 360 (* 60 (/ (- g b) (- max min))))) | ||
| 46 | ((= max g) | ||
| 47 | (+ 120 (* 60 (/ (- b r) (- max min))))) | ||
| 48 | ((= max b) | ||
| 49 | (+ 240 (* 60 (/ (- r g) (- max min))))))) | ||
| 50 | 360) | ||
| 51 | (if (= max 0) | ||
| 52 | 0 | ||
| 53 | (- 1 (/ min max))) | ||
| 54 | (/ max 255.0)))) | ||
| 55 | |||
| 56 | (defun rgb->hsl (red green blue) | ||
| 57 | "Convert RED GREEN BLUE colors to their HSL representation. | ||
| 58 | RED, GREEN and BLUE must be between 0 and 255." | ||
| 59 | (let* ((r (/ red 255.0)) | ||
| 60 | (g (/ green 255.0)) | ||
| 61 | (b (/ blue 255.0)) | ||
| 62 | (max (max r g b)) | ||
| 63 | (min (min r g b)) | ||
| 64 | (delta (- max min)) | ||
| 65 | (l (/ (+ max min) 2.0))) | ||
| 66 | (list | ||
| 67 | (if (= max min) | ||
| 68 | 0 | ||
| 69 | (* 2 float-pi | ||
| 70 | (/ (cond ((= max r) | ||
| 71 | (+ (/ (- g b) delta) (if (< g b) 6 0))) | ||
| 72 | ((= max g) | ||
| 73 | (+ (/ (- b r) delta) 2)) | ||
| 74 | (t | ||
| 75 | (+ (/ (- r g) delta) 4))) | ||
| 76 | 6))) | ||
| 77 | (if (= max min) | ||
| 78 | 0 | ||
| 79 | (if (> l 0.5) | ||
| 80 | (/ delta (- 2 (+ max min))) | ||
| 81 | (/ delta (+ max min)))) | ||
| 82 | l))) | ||
| 83 | |||
| 84 | (defun rgb->xyz (red green blue) | ||
| 85 | "Converts RED GREEN BLUE colors to CIE XYZ representation. | ||
| 86 | RED, BLUE and GREEN must be between 0 and 1." | ||
| 87 | (let ((r (if (<= red 0.04045) | ||
| 88 | (/ red 12.95) | ||
| 89 | (expt (/ (+ red 0.055) 1.055) 2.4))) | ||
| 90 | (g (if (<= green 0.04045) | ||
| 91 | (/ green 12.95) | ||
| 92 | (expt (/ (+ green 0.055) 1.055) 2.4))) | ||
| 93 | (b (if (<= blue 0.04045) | ||
| 94 | (/ blue 12.95) | ||
| 95 | (expt (/ (+ blue 0.055) 1.055) 2.4)))) | ||
| 96 | (list (+ (* 0.4124564 r) (* 0.3575761 g) (* 0.1804375 b)) | ||
| 97 | (+ (* 0.21266729 r) (* 0.7151522 g) (* 0.0721750 b)) | ||
| 98 | (+ (* 0.0193339 r) (* 0.1191920 g) (* 0.9503041 b))))) | ||
| 99 | |||
| 100 | (defun xyz->rgb (X Y Z) | ||
| 101 | "Converts CIE XYZ colors to RGB." | ||
| 102 | (let ((r (+ (* 3.2404542 X) (* -1.5371385 Y) (* -0.4985314 Z))) | ||
| 103 | (g (+ (* -0.9692660 X) (* 1.8760108 Y) (* 0.0415560 Z))) | ||
| 104 | (b (+ (* 0.0556434 X) (* -0.2040259 Y) (* 1.0572252 Z)))) | ||
| 105 | (list (if (<= r 0.0031308) | ||
| 106 | (* 12.92 r) | ||
| 107 | (- (* 1.055 (expt r (/ 1 2.4))) 0.055)) | ||
| 108 | (if (<= g 0.0031308) | ||
| 109 | (* 12.92 g) | ||
| 110 | (- (* 1.055 (expt g (/ 1 2.4))) 0.055)) | ||
| 111 | (if (<= b 0.0031308) | ||
| 112 | (* 12.92 b) | ||
| 113 | (- (* 1.055 (expt b (/ 1 2.4))) 0.055))))) | ||
| 114 | |||
| 115 | (defconst color-lab-d65-xyz '(0.950455 1.0 1.088753) | ||
| 116 | "D65 white point in CIE XYZ.") | ||
| 117 | |||
| 118 | (defconst color-lab-ε (/ 216 24389.0)) | ||
| 119 | (defconst color-lab-κ (/ 24389 27.0)) | ||
| 120 | |||
| 121 | (defun xyz->lab (X Y Z &optional white-point) | ||
| 122 | "Converts CIE XYZ to CIE L*a*b*. | ||
| 123 | WHITE-POINT can be specified as (X Y Z) white point to use. If | ||
| 124 | none is set, `color-lab-d65-xyz' is used." | ||
| 125 | (destructuring-bind (Xr Yr Zr) (or white-point color-lab-d65-xyz) | ||
| 126 | (let* ((xr (/ X Xr)) | ||
| 127 | (yr (/ Y Yr)) | ||
| 128 | (zr (/ Z Zr)) | ||
| 129 | (fx (if (> xr color-lab-ε) | ||
| 130 | (expt xr (/ 1 3.0)) | ||
| 131 | (/ (+ (* color-lab-κ xr) 16) 116.0))) | ||
| 132 | (fy (if (> yr color-lab-ε) | ||
| 133 | (expt yr (/ 1 3.0)) | ||
| 134 | (/ (+ (* color-lab-κ yr) 16) 116.0))) | ||
| 135 | (fz (if (> zr color-lab-ε) | ||
| 136 | (expt zr (/ 1 3.0)) | ||
| 137 | (/ (+ (* color-lab-κ zr) 16) 116.0)))) | ||
| 138 | (list | ||
| 139 | (- (* 116 fy) 16) ; L | ||
| 140 | (* 500 (- fx fy)) ; a | ||
| 141 | (* 200 (- fy fz)))))) ; b | ||
| 142 | |||
| 143 | (defun lab->xyz (L a b &optional white-point) | ||
| 144 | "Converts CIE L*a*b* to CIE XYZ. | ||
| 145 | WHITE-POINT can be specified as (X Y Z) white point to use. If | ||
| 146 | none is set, `color-lab-d65-xyz' is used." | ||
| 147 | (destructuring-bind (Xr Yr Zr) (or white-point color-lab-d65-xyz) | ||
| 148 | (let* ((fy (/ (+ L 16) 116.0)) | ||
| 149 | (fz (- fy (/ b 200.0))) | ||
| 150 | (fx (+ (/ a 500.0) fy)) | ||
| 151 | (xr (if (> (expt fx 3) color-lab-ε) | ||
| 152 | (expt fx 3) | ||
| 153 | (/ (- (* fx 116) 16) color-lab-κ))) | ||
| 154 | (yr (if (> L (* color-lab-κ color-lab-ε)) | ||
| 155 | (expt (/ (+ L 16) 116.0) 3) | ||
| 156 | (/ L color-lab-κ))) | ||
| 157 | (zr (if (> (expt fz 3) color-lab-ε) | ||
| 158 | (expt fz 3) | ||
| 159 | (/ (- (* 116 fz) 16) color-lab-κ)))) | ||
| 160 | (list (* xr Xr) ; X | ||
| 161 | (* yr Yr) ; Y | ||
| 162 | (* zr Zr))))) ; Z | ||
| 163 | |||
| 164 | (defun rgb->lab (red green blue) | ||
| 165 | "Converts RGB to CIE L*a*b*." | ||
| 166 | (apply 'xyz->lab (rgb->xyz red green blue))) | ||
| 167 | |||
| 168 | (defun rgb->normalize (color) | ||
| 169 | "Normalize a RGB color to values between [0,1]." | ||
| 170 | (mapcar (lambda (x) (/ x 65535.0)) (x-color-values color))) | ||
| 171 | |||
| 172 | (defun lab->rgb (L a b) | ||
| 173 | "Converts CIE L*a*b* to RGB." | ||
| 174 | (apply 'xyz->rgb (lab->xyz L a b))) | ||
| 175 | |||
| 176 | (defun color-lab-ciede2000 (color1 color2 &optional kL kC kH) | ||
| 177 | "Computes the CIEDE2000 color distance between COLOR1 and COLOR2. | ||
| 178 | Colors must be in CIE L*a*b* format." | ||
| 179 | (destructuring-bind (L₁ a₁ b₁) color1 | ||
| 180 | (destructuring-bind (L₂ a₂ b₂) color2 | ||
| 181 | (let* ((kL (or kL 1)) | ||
| 182 | (kC (or kC 1)) | ||
| 183 | (kH (or kH 1)) | ||
| 184 | (C₁ (sqrt (+ (expt a₁ 2) (expt b₁ 2)))) | ||
| 185 | (C₂ (sqrt (+ (expt a₂ 2) (expt b₂ 2)))) | ||
| 186 | (C̄ (/ (+ C₁ C₂) 2.0)) | ||
| 187 | (G (* 0.5 (- 1 (sqrt (/ (expt C̄ 7) (+ (expt C̄ 7) (expt 25 7))))))) | ||
| 188 | (a′₁ (* (+ 1 G) a₁)) | ||
| 189 | (a′₂ (* (+ 1 G) a₂)) | ||
| 190 | (C′₁ (sqrt (+ (expt a′₁ 2) (expt b₁ 2)))) | ||
| 191 | (C′₂ (sqrt (+ (expt a′₂ 2) (expt b₂ 2)))) | ||
| 192 | (h′₁ (if (and (= b₁ 0) (= a′₁ 0)) | ||
| 193 | 0 | ||
| 194 | (let ((v (atan b₁ a′₁))) | ||
| 195 | (if (< v 0) | ||
| 196 | (+ v (* 2 float-pi)) | ||
| 197 | v)))) | ||
| 198 | (h′₂ (if (and (= b₂ 0) (= a′₂ 0)) | ||
| 199 | 0 | ||
| 200 | (let ((v (atan b₂ a′₂))) | ||
| 201 | (if (< v 0) | ||
| 202 | (+ v (* 2 float-pi)) | ||
| 203 | v)))) | ||
| 204 | (ΔL′ (- L₂ L₁)) | ||
| 205 | (ΔC′ (- C′₂ C′₁)) | ||
| 206 | (Δh′ (cond ((= (* C′₁ C′₂) 0) | ||
| 207 | 0) | ||
| 208 | ((<= (abs (- h′₂ h′₁)) float-pi) | ||
| 209 | (- h′₂ h′₁)) | ||
| 210 | ((> (- h′₂ h′₁) float-pi) | ||
| 211 | (- (- h′₂ h′₁) (* 2 float-pi))) | ||
| 212 | ((< (- h′₂ h′₁) (- float-pi)) | ||
| 213 | (+ (- h′₂ h′₁) (* 2 float-pi))))) | ||
| 214 | (ΔH′ (* 2 (sqrt (* C′₁ C′₂)) (sin (/ Δh′ 2.0)))) | ||
| 215 | (L̄′ (/ (+ L₁ L₂) 2.0)) | ||
| 216 | (C̄′ (/ (+ C′₁ C′₂) 2.0)) | ||
| 217 | (h̄′ (cond ((= (* C′₁ C′₂) 0) | ||
| 218 | (+ h′₁ h′₂)) | ||
| 219 | ((<= (abs (- h′₁ h′₂)) float-pi) | ||
| 220 | (/ (+ h′₁ h′₂) 2.0)) | ||
| 221 | ((< (+ h′₁ h′₂) (* 2 float-pi)) | ||
| 222 | (/ (+ h′₁ h′₂ (* 2 float-pi)) 2.0)) | ||
| 223 | ((>= (+ h′₁ h′₂) (* 2 float-pi)) | ||
| 224 | (/ (+ h′₁ h′₂ (* -2 float-pi)) 2.0)))) | ||
| 225 | (T (+ 1 | ||
| 226 | (- (* 0.17 (cos (- h̄′ (degrees-to-radians 30))))) | ||
| 227 | (* 0.24 (cos (* h̄′ 2))) | ||
| 228 | (* 0.32 (cos (+ (* h̄′ 3) (degrees-to-radians 6)))) | ||
| 229 | (- (* 0.20 (cos (- (* h̄′ 4) (degrees-to-radians 63))))))) | ||
| 230 | (Δθ (* (degrees-to-radians 30) (exp (- (expt (/ (- h̄′ (degrees-to-radians 275)) (degrees-to-radians 25)) 2))))) | ||
| 231 | (Rc (* 2 (sqrt (/ (expt C̄′ 7) (+ (expt C̄′ 7) (expt 25 7)))))) | ||
| 232 | (Sl (+ 1 (/ (* 0.015 (expt (- L̄′ 50) 2)) (sqrt (+ 20 (expt (- L̄′ 50) 2)))))) | ||
| 233 | (Sc (+ 1 (* C̄′ 0.045))) | ||
| 234 | (Sh (+ 1 (* 0.015 C̄′ T))) | ||
| 235 | (Rt (- (* (sin (* Δθ 2)) Rc)))) | ||
| 236 | (sqrt (+ (expt (/ ΔL′ (* Sl kL)) 2) | ||
| 237 | (expt (/ ΔC′ (* Sc kC)) 2) | ||
| 238 | (expt (/ ΔH′ (* Sh kH)) 2) | ||
| 239 | (* Rt (/ ΔC′ (* Sc kC)) (/ ΔH′ (* Sh kH))))))))) | ||
| 240 | |||
| 241 | (provide 'color-lab) | ||
diff --git a/lisp/gnus/shr-color.el b/lisp/gnus/shr-color.el new file mode 100644 index 00000000000..127dc68191a --- /dev/null +++ b/lisp/gnus/shr-color.el | |||
| @@ -0,0 +1,179 @@ | |||
| 1 | ;;; shr-color.el --- Simple HTML Renderer color management | ||
| 2 | |||
| 3 | ;; Copyright (C) 2010 Free Software Foundation, Inc. | ||
| 4 | |||
| 5 | ;; Author: Julien Danjou <julien@danjou.info> | ||
| 6 | ;; Keywords: html | ||
| 7 | |||
| 8 | ;; This file is part of GNU Emacs. | ||
| 9 | |||
| 10 | ;; GNU Emacs is free software: you can redistribute it and/or modify | ||
| 11 | ;; it under the terms of the GNU General Public License as published by | ||
| 12 | ;; the Free Software Foundation, either version 3 of the License, or | ||
| 13 | ;; (at your option) any later version. | ||
| 14 | |||
| 15 | ;; GNU Emacs is distributed in the hope that it will be useful, | ||
| 16 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 17 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 18 | ;; GNU General Public License for more details. | ||
| 19 | |||
| 20 | ;; You should have received a copy of the GNU General Public License | ||
| 21 | ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. | ||
| 22 | |||
| 23 | ;;; Commentary: | ||
| 24 | |||
| 25 | ;; This package handles colors display for shr. | ||
| 26 | |||
| 27 | ;;; Code: | ||
| 28 | |||
| 29 | (require 'color-lab) | ||
| 30 | |||
| 31 | (defgroup shr-color nil | ||
| 32 | "Simple HTML Renderer colors" | ||
| 33 | :group 'shr) | ||
| 34 | |||
| 35 | (defcustom shr-color-visible-luminance-min 40 | ||
| 36 | "Minimum luminance distance between two colors to be considered visible. | ||
| 37 | Must be between 0 and 100." | ||
| 38 | :group 'shr | ||
| 39 | :type 'float) | ||
| 40 | |||
| 41 | (defcustom shr-color-visible-distance-min 5 | ||
| 42 | "Minimum color distance between two colors to be considered visible. | ||
| 43 | This value is used to compare result for `ciede2000'. Its an | ||
| 44 | absolute value without any unit." | ||
| 45 | :group 'shr | ||
| 46 | :type 'integer) | ||
| 47 | |||
| 48 | (defun shr-color-relative-to-absolute (number) | ||
| 49 | "Convert a relative NUMBER to absolute. If NUMBER is absolute, return NUMBER. | ||
| 50 | This will convert \"80 %\" to 204, \"100 %\" to 255 but \"123\" to \"123\"." | ||
| 51 | (let ((string-length (- (length number) 1))) | ||
| 52 | ;; Is this a number with %? | ||
| 53 | (if (eq (elt number string-length) ?%) | ||
| 54 | (/ (* (string-to-number (substring number 0 string-length)) 255) 100) | ||
| 55 | (string-to-number number)))) | ||
| 56 | |||
| 57 | (defun shr-color-hsl-to-rgb-fractions (h s l) | ||
| 58 | "Convert H S L to fractional RGB values." | ||
| 59 | (let (m1 m2) | ||
| 60 | (if (<= l 0.5) | ||
| 61 | (setq m2 (* l (+ s 1))) | ||
| 62 | (setq m2 (- (+ l s) (* l s)))) | ||
| 63 | (setq m1 (- (* l 2) m2)) | ||
| 64 | (list (rainbow-hue-to-rgb m1 m2 (+ h (/ 1 3.0))) | ||
| 65 | (rainbow-hue-to-rgb m1 m2 h) | ||
| 66 | (rainbow-hue-to-rgb m1 m2 (- h (/ 1 3.0)))))) | ||
| 67 | |||
| 68 | (defun shr-color->hexadecimal (color) | ||
| 69 | "Convert any color format to hexadecimal representation. | ||
| 70 | Like rgb() or hsl()." | ||
| 71 | (when color | ||
| 72 | (cond ((or (string-match | ||
| 73 | "rgb(\s*\\([0-9]\\{1,3\\}\\(?:\s*%\\)?\\)\s*,\s*\\([0-9]\\{1,3\\}\\(?:\s*%\\)?\\)\s*,\s*\\([0-9]\\{1,3\\}\\(?:\s*%\\)?\\)\s*)" | ||
| 74 | color) | ||
| 75 | (string-match | ||
| 76 | "rgba(\s*\\([0-9]\\{1,3\\}\\(?:\s*%\\)?\\)\s*,\s*\\([0-9]\\{1,3\\}\\(?:\s*%\\)?\\)\s*,\s*\\([0-9]\\{1,3\\}\\(?:\s*%\\)?\\)\s*,\s*[0-9]*\.?[0-9]+\s*%?\s*)" | ||
| 77 | color)) | ||
| 78 | (format "#%02X%02X%02X" | ||
| 79 | (shr-color-relative-to-absolute (match-string-no-properties 1 color)) | ||
| 80 | (shr-color-relative-to-absolute (match-string-no-properties 2 color)) | ||
| 81 | (shr-color-relative-to-absolute (match-string-no-properties 3 color)))) | ||
| 82 | ((or (string-match | ||
| 83 | "hsl(\s*\\([0-9]\\{1,3\\}\\)\s*,\s*\\([0-9]\\{1,3\\}\\)\s*%\s*,\s*\\([0-9]\\{1,3\\}\\)\s*%\s*)" | ||
| 84 | color) | ||
| 85 | (string-match | ||
| 86 | "hsla(\s*\\([0-9]\\{1,3\\}\\)\s*,\s*\\([0-9]\\{1,3\\}\\)\s*%\s*,\s*\\([0-9]\\{1,3\\}\\)\s*%\s*,\s*[0-9]*\.?[0-9]+\s*%?\s*)" | ||
| 87 | color)) | ||
| 88 | (let ((h (/ (string-to-number (match-string-no-properties 1 color)) 360.0)) | ||
| 89 | (s (/ (string-to-number (match-string-no-properties 2 color)) 100.0)) | ||
| 90 | (l (/ (string-to-number (match-string-no-properties 3 color)) 100.0))) | ||
| 91 | (destructuring-bind (r g b) | ||
| 92 | (rainbow-hsl-to-rgb-fractions h s l) | ||
| 93 | (format "#%02X%02X%02X" (* r 255) (* g 255) (* b 255))))) | ||
| 94 | (t | ||
| 95 | color)))) | ||
| 96 | |||
| 97 | (defun set-minimum-interval (val1 val2 min max interval &optional fixed) | ||
| 98 | "Set minimum interval between VAL1 and VAL2 to INTERVAL. | ||
| 99 | The values are bound by MIN and MAX. | ||
| 100 | If FIXED is t, then val1 will not be touched." | ||
| 101 | (let ((diff (abs (- val1 val2)))) | ||
| 102 | (unless (>= diff interval) | ||
| 103 | (if fixed | ||
| 104 | (let* ((missing (- interval diff)) | ||
| 105 | ;; If val2 > val1, try to increase val2 | ||
| 106 | ;; That's the "good direction" | ||
| 107 | (val2-good-direction | ||
| 108 | (if (> val2 val1) | ||
| 109 | (min max (+ val2 missing)) | ||
| 110 | (max min (- val2 missing)))) | ||
| 111 | (diff-val2-good-direction-val1 (abs (- val2-good-direction val1)))) | ||
| 112 | (if (>= diff-val2-good-direction-val1 interval) | ||
| 113 | (setq val2 val2-good-direction) | ||
| 114 | ;; Good-direction is not so good, compute bad-direction | ||
| 115 | (let* ((val2-bad-direction | ||
| 116 | (if (> val2 val1) | ||
| 117 | (max min (- val1 interval)) | ||
| 118 | (min max (+ val1 interval)))) | ||
| 119 | (diff-val2-bad-direction-val1 (abs (- val2-bad-direction val1)))) | ||
| 120 | (if (>= diff-val2-bad-direction-val1 interval) | ||
| 121 | (setq val2 val2-bad-direction) | ||
| 122 | ;; Still not good, pick the best and prefer good direction | ||
| 123 | (setq val2 | ||
| 124 | (if (>= diff-val2-good-direction-val1 diff-val2-bad-direction-val1) | ||
| 125 | val2-good-direction | ||
| 126 | val2-bad-direction)))))) | ||
| 127 | ;; No fixed, move val1 and val2 | ||
| 128 | (let ((missing (/ (- interval diff) 2.0))) | ||
| 129 | (if (< val1 val2) | ||
| 130 | (setq val1 (max min (- val1 missing)) | ||
| 131 | val2 (min max (+ val2 missing))) | ||
| 132 | (setq val2 (max min (- val2 missing)) | ||
| 133 | val1 (min max (+ val1 missing)))) | ||
| 134 | (setq diff (abs (- val1 val2))) ; Recompute diff | ||
| 135 | (unless (>= diff interval) | ||
| 136 | ;; Not ok, we hit a boundary | ||
| 137 | (let ((missing (- interval diff))) | ||
| 138 | (cond ((= val1 min) | ||
| 139 | (setq val2 (+ val2 missing))) | ||
| 140 | ((= val2 min) | ||
| 141 | (setq val1 (+ val1 missing))) | ||
| 142 | ((= val1 max) | ||
| 143 | (setq val2 (- val2 missing))) | ||
| 144 | ((= val2 max) | ||
| 145 | (setq val1 (- val1 missing))))))))) | ||
| 146 | (list val1 val2))) | ||
| 147 | |||
| 148 | (defun shr-color-visible (bg fg &optional fixed-background) | ||
| 149 | "Check that BG and FG colors are visible if they are drawn on each other. | ||
| 150 | Return t if they are. If they are too similar, two new colors are | ||
| 151 | returned instead. | ||
| 152 | If FIXED-BACKGROUND is set, and if the color are not visible, a | ||
| 153 | new background color will not be computed. Only the foreground | ||
| 154 | color will be adapted to be visible on BG." | ||
| 155 | ;; Convert fg and bg to CIE Lab | ||
| 156 | (let* ((fg-lab (apply 'rgb->lab (rgb->normalize fg))) | ||
| 157 | (bg-lab (apply 'rgb->lab (rgb->normalize bg))) | ||
| 158 | ;; Compute color distance using CIE DE 2000 | ||
| 159 | (fg-bg-distance (color-lab-ciede2000 fg-lab bg-lab)) | ||
| 160 | ;; Compute luminance distance (substract L component) | ||
| 161 | (luminance-distance (abs (- (car fg-lab) (car bg-lab))))) | ||
| 162 | (if (and (>= fg-bg-distance shr-color-visible-distance-min) | ||
| 163 | (>= luminance-distance shr-color-visible-luminance-min)) | ||
| 164 | (list bg fg) | ||
| 165 | ;; Not visible, try to change luminance to make them visible | ||
| 166 | (let ((Ls (set-minimum-interval (car bg-lab) (car fg-lab) 0 100 | ||
| 167 | shr-color-visible-luminance-min | ||
| 168 | fixed-background))) | ||
| 169 | (setcar bg-lab (car Ls)) | ||
| 170 | (setcar fg-lab (cadr Ls)) | ||
| 171 | (list | ||
| 172 | (apply 'format "#%02x%02x%02x" | ||
| 173 | (mapcar (lambda (x) (* (max (min 1 x) 0) 255)) (apply 'lab->rgb bg-lab))) | ||
| 174 | (apply 'format "#%02x%02x%02x" | ||
| 175 | (mapcar (lambda (x) (* (max (min 1 x) 0) 255)) (apply 'lab->rgb fg-lab)))))))) | ||
| 176 | |||
| 177 | (provide 'shr-color) | ||
| 178 | |||
| 179 | ;;; shr-color.el ends here | ||
diff --git a/lisp/gnus/shr.el b/lisp/gnus/shr.el index 6e5800ac1c7..60fa1271939 100644 --- a/lisp/gnus/shr.el +++ b/lisp/gnus/shr.el | |||
| @@ -517,6 +517,31 @@ START, and END." | |||
| 517 | (defun shr-tag-s (cont) | 517 | (defun shr-tag-s (cont) |
| 518 | (shr-fontize-cont cont 'strike-through)) | 518 | (shr-fontize-cont cont 'strike-through)) |
| 519 | 519 | ||
| 520 | (autoload 'shr-color-visible "shr-color") | ||
| 521 | (defun shr-tag-color-check (fg &optional bg) | ||
| 522 | "Check that FG is visible on BG." | ||
| 523 | (shr-color-visible (or (shr-color->hexadecimal bg) | ||
| 524 | (frame-parameter nil 'background-color)) | ||
| 525 | (shr-color->hexadecimal fg) (not bg))) | ||
| 526 | |||
| 527 | (defun shr-tag-insert-color-overlay (color start end) | ||
| 528 | (when color | ||
| 529 | (let ((overlay (make-overlay start end))) | ||
| 530 | (overlay-put overlay 'face (cons 'foreground-color | ||
| 531 | (cadr (shr-tag-color-check color))))))) | ||
| 532 | |||
| 533 | (defun shr-tag-span (cont) | ||
| 534 | (let ((start (point)) | ||
| 535 | (color (cdr (assq 'color (shr-parse-style (cdr (assq :style cont))))))) | ||
| 536 | (shr-generic cont) | ||
| 537 | (shr-tag-insert-color-overlay color start (point)))) | ||
| 538 | |||
| 539 | (defun shr-tag-font (cont) | ||
| 540 | (let ((start (point)) | ||
| 541 | (color (cdr (assq :color cont)))) | ||
| 542 | (shr-generic cont) | ||
| 543 | (shr-tag-insert-color-overlay color start (point)))) | ||
| 544 | |||
| 520 | (defun shr-parse-style (style) | 545 | (defun shr-parse-style (style) |
| 521 | (when style | 546 | (when style |
| 522 | (let ((plist nil)) | 547 | (let ((plist nil)) |