branch: externals/compat
commit 8d896bf8f7542b14480c005e8ed5e87e06a506fc
Author: Daniel Mendler <m...@daniel-mendler.de>
Commit: Daniel Mendler <m...@daniel-mendler.de>

    compat-30: Add oklab color functions
    
    Co-authored-by: Elijah Gabe Pérez <eg642...@gmail.com>
---
 NEWS.org        |  4 ++++
 compat-30.el    | 44 ++++++++++++++++++++++++++++++++++++++++++++
 compat-tests.el | 40 ++++++++++++++++++++++++++++++++++++++++
 compat.texi     | 24 ++++++++++++++++++++++++
 4 files changed, 112 insertions(+)

diff --git a/NEWS.org b/NEWS.org
index 5a5832f5c8..34c30818e9 100644
--- a/NEWS.org
+++ b/NEWS.org
@@ -2,6 +2,10 @@
 #+link:    compat-gh   https://github.com/emacs-compat/compat/issues/
 #+options: toc:nil num:nil author:nil
 
+* Development
+
+- compat-30: Add oklab color functions.
+
 * Release of "Compat" Version 30.0.2.0
 
 - compat-30: Rename =trusted-files= to =trusted-content=.
diff --git a/compat-30.el b/compat-30.el
index cb6e44249d..2decb93711 100644
--- a/compat-30.el
+++ b/compat-30.el
@@ -215,6 +215,50 @@ details."
       (funcall completion-lazy-hilit-fn (copy-sequence str))
     str))
 
+;;;; Defined in color.el
+
+(compat-defun color-oklab-to-xyz (l a b) ;; <compat-tests:color-oklab-to-xyz>
+  "Convert the OkLab color represented by L A B to CIE XYZ.
+Oklab is a perceptual color space created by Björn Ottosson
+<https://bottosson.github.io/posts/oklab/>.  It has the property that
+changes in the hue and saturation of a color can be made while maintaining
+the same perceived lightness."
+  :feature color
+  (let ((ll (expt (+ (* 1.0 l) (* 0.39633779 a) (* 0.21580376 b)) 3))
+        (mm (expt (+ (* 1.00000001 l) (* -0.10556134 a) (* -0.06385417 b)) 3))
+        (ss (expt (+ (* 1.00000005 l) (* -0.08948418 a) (* -1.29148554 b)) 3)))
+    (list (+ (* ll 1.22701385) (* mm -0.55779998) (* ss 0.28125615))
+          (+ (* ll -0.04058018) (* mm 1.11225687) (* ss -0.07167668))
+          (+ (* ll -0.07638128) (* mm -0.42148198) (* ss 1.58616322)))))
+
+(compat-defun color-xyz-to-oklab (x y z) ;; <compat-tests:color-xyz-to-oklab>
+  "Convert the CIE XYZ color represented by X Y Z to Oklab."
+  :feature color
+  (let ((ll (+ (* x 0.8189330101) (* y 0.3618667424) (* z -0.1288597137)))
+        (mm (+ (* x 0.0329845436) (* y 0.9293118715) (* z 0.0361456387)))
+        (ss (+ (* x 0.0482003018) (* y 0.2643662691) (* z 0.6338517070))))
+    (let*
+        ((cube-root (lambda (f)
+                      (if (< f 0)
+                          (- (expt (- f) (/ 1.0 3.0)))
+                        (expt f (/ 1.0 3.0)))))
+         (lll (funcall cube-root ll))
+         (mmm (funcall cube-root mm))
+         (sss (funcall cube-root ss)))
+      (list (+ (* lll 0.2104542553) (* mmm 0.7936177850) (* sss -0.0040720468))
+            (+ (* lll 1.9779984951) (* mmm -2.4285922050) (* sss 0.4505937099))
+            (+ (* lll 0.0259040371) (* mmm 0.7827717662) (* sss 
-0.8086757660))))))
+
+(compat-defun color-oklab-to-srgb (l a b) ;; <compat-tests:color-oklab-to-srgb>
+  "Convert the Oklab color represented by L A B to sRGB."
+  :feature color
+  (apply #'color-xyz-to-srgb (color-oklab-to-xyz l a b)))
+
+(compat-defun color-srgb-to-oklab (r g b) ;; <compat-tests:color-srgb-to-oklab>
+  "Convert the sRGB color R G B to Oklab."
+  :feature color
+  (apply #'color-xyz-to-oklab (color-srgb-to-xyz r g b)))
+
 ;;;; Defined in subr.el
 
 (compat-defmacro static-if (condition then-form &rest else-forms) ;; 
<compat-tests:static-if>
diff --git a/compat-tests.el b/compat-tests.el
index 223d0dde69..baba1c296a 100644
--- a/compat-tests.el
+++ b/compat-tests.el
@@ -56,6 +56,7 @@
 (require 'time-date)
 (require 'image)
 (require 'text-property-search nil t)
+(require 'color)
 
 ;; Setup tramp mock
 (require 'tramp)
@@ -2681,6 +2682,45 @@
   (should-not (color-values-from-color-spec "rgbi : 0/0/0"))
   (should-not (color-values-from-color-spec "rgbi:0/0.5/10")))
 
+(defun compat--color-approx-equal (color1 color2)
+  "Return t if COLOR1 and COLOR2 are approximately equal."
+  (seq-every-p
+   (lambda (x) (< (abs x) 0.00001))
+   (cl-mapcar #'- color1 color2)))
+
+(ert-deftest compat-color-oklab-to-xyz ()
+  (should (compat--color-approx-equal (compat-call color-oklab-to-xyz 0 0 0) 
'(0.0 0.0 0.0)))
+  (should (compat--color-approx-equal (compat-call color-oklab-to-xyz 1.0 0.0 
0.0)
+                                     '(0.95047005 1.0 1.0883001)))
+  (should (compat--color-approx-equal (compat-call color-oklab-to-xyz 0.450 
1.236 -0.019) '(1.000604 -0.000008 -0.000038)))
+  (should (compat--color-approx-equal (compat-call color-oklab-to-xyz 0.922 
-0.671 0.263) '(0.000305 1.000504 0.000898)))
+  (should (compat--color-approx-equal (compat-call color-oklab-to-xyz 0.153 
-1.415 -0.449) '(0.000590 0.000057 1.001650))))
+
+(ert-deftest compat-color-xyz-to-oklab ()
+  (should (compat--color-approx-equal (compat-call color-xyz-to-oklab 0 0 0) 
'(0.0 0.0 0.0)))
+  (should (compat--color-approx-equal (compat-call color-xyz-to-oklab 0.95 1.0 
1.089)
+                                     '(0.999969 -0.000258 -0.000115)))
+  (should (compat--color-approx-equal (compat-call color-xyz-to-oklab 1.0 0.0 
0.0)
+                                     '(0.449932 1.235710 -0.019028)))
+  (should (compat--color-approx-equal (compat-call color-xyz-to-oklab 0.0 1.0 
0.0)
+                                     '(0.921817 -0.671238 0.263324)))
+  (should (compat--color-approx-equal (compat-call color-xyz-to-oklab 0.0 0.0 
1.0)
+                                     '(0.152603 -1.414997 -0.448927))))
+
+(ert-deftest compat-color-srgb-to-oklab ()
+  (should (equal (compat-call color-srgb-to-oklab 0 0 0) '(0.0 0.0 0.0)))
+  (should
+   (compat--color-approx-equal (compat-call color-srgb-to-oklab 0 0 1) 
'(0.451978 -0.032430 -0.311611)))
+  (should
+   (compat--color-approx-equal (compat-call color-srgb-to-oklab 0.1 0.2 0.3) 
'(0.313828 -0.019091 -0.052561))))
+
+(ert-deftest compat-color-oklab-to-srgb ()
+  (should (equal (compat-call color-oklab-to-srgb 0 0 0) '(0.0 0.0 0.0)))
+  (should
+   (compat--color-approx-equal (compat-call color-oklab-to-srgb 0.451978 
-0.032430 -0.311611) '(0.0 0.0 1.0)))
+  (should
+   (compat--color-approx-equal (compat-call color-oklab-to-srgb 0.313828 
-0.019091 -0.052561) '(0.1 0.2 0.3))))
+
 (ert-deftest compat-lookup-key ()
   (let ((a-map (make-sparse-keymap))
         (b-map (make-sparse-keymap)))
diff --git a/compat.texi b/compat.texi
index c9ed82e322..2e08c91fb5 100644
--- a/compat.texi
+++ b/compat.texi
@@ -3355,6 +3355,30 @@ Return non-nil if we trust the contents of the current 
buffer.  Here,
 also @code{trusted-content}.
 @end defun
 
+@c based on lisp/color.el
+@defun color-oklab-to-xyz l a b
+Convert the OkLab color represented by @var{l} @var{a} @var{b} to CIE XYZ.
+Oklab is a perceptual color space created by Björn Ottosson
+<https://bottosson.github.io/posts/oklab/>.  It has the property that
+changes in the hue and saturation of a color can be made while maintaining
+the same perceived lightness.
+@end defun
+
+@c based on lisp/color.el
+@defun color-xyz-to-oklab x y z
+Convert the CIE XYZ color represented by @var{x} @var{y} @var{z} to Oklab.
+@end defun
+
+@c based on lisp/color.el
+@defun color-oklab-to-srgb l a b
+Convert the Oklab color represented by @var{l} @var{a} @var{b} to sRGB.
+@end defun
+
+@c based on lisp/color.el
+@defun color-srgb-to-oklab r g b
+Convert the sRGB color @var{r} @var{g} @var{b} to Oklab.
+@end defun
+
 @c copied from lispref/nonascii.texi
 @defun char-to-name char
 This function returns the Unicode name of @var{char}.  It returns

Reply via email to