r8073: add def-cached-vector
[kmrcl.git] / color.lisp
index a3033bd864d1991492dc722f2af495330229e8d8..d674886165f061ffa5424ad2f037305339bfd65c 100644 (file)
 ;; values.\r
 \r
 (defun hsv->rgb (h s v) \r
+  (declare (optimize (speed 3) (safety 0)))\r
+  (when (zerop s)\r
+    (return-from hsv->rgb (values v v v)))\r
+\r
   (while (minusp h)\r
          (incf h 360))\r
-  (while (> h 360)\r
+  (while (>= h 360)\r
          (decf h 360))\r
+  \r
+  (let ((h-pos (/ h 60)))\r
+    (multiple-value-bind (h-int h-frac) (truncate h-pos)\r
+      (declare (fixnum h-int))\r
+      (let ((p (* v (- 1 s)))\r
+            (q (* v (- 1 (* s h-frac))))\r
+            (t_ (* v (- 1 (* s (- 1 h-frac)))))\r
+            r g b)\r
+        \r
+        (cond\r
+         ((zerop h-int)\r
+          (setf r v\r
+                g t_  \r
+                b p))\r
+         ((= 1 h-int)\r
+          (setf r q\r
+                g v\r
+                b p))\r
+         ((= 2 h-int)\r
+          (setf r p\r
+                g v\r
+                b t_))\r
+         ((= 3 h-int)\r
+          (setf r p\r
+                g q\r
+                b v))\r
+         ((= 4 h-int)\r
+          (setf r t_\r
+                g p\r
+                b v))\r
+         ((= 5 h-int)\r
+          (setf r v\r
+                g p\r
+                b q)))\r
+        (values r g b)))))\r
 \r
-  (let (r g b)\r
-    (cond\r
-     ((< h 120)\r
-      (setf r (/ (- 120 h) 60)\r
-            g (/ h 60)\r
-            b 0))\r
-     ((< h 240)\r
-      (setf r 0\r
-            g (/ (- 240 h) 60)\r
-            b (/ (- h 120) 60)))\r
-     (t\r
-      (setf r (/ (- h 240) 60)\r
-            g 0\r
-            b (/ (- 360 h) 60))))\r
-    (setf r (min r 1)\r
-          g (min g 1)\r
-          b (min b 1))\r
 \r
-    (values (* (+ 1 (* s r) (- s)) v)\r
-            (* (+ 1 (* s g) (- s)) v)\r
-            (* (+ 1 (* s b) (- s)) v))))\r
+(defun hsv255->rgb255 (h s v) \r
+  (declare (optimize (speed 3) (safety 0) (space 0) (compilation-speed 0)))\r
 \r
+  (when (zerop s)\r
+    (return-from hsv255->rgb255 (values v v v)))\r
 \r
-(defun rgb->hsv (r g b)\r
+  (locally (declare (type fixnum h s v))\r
+    (while (minusp h)\r
+      (incf h 360))\r
+    (while (>= h 360)\r
+      (decf h 360))\r
+    \r
+    (let ((h-pos (/ h 60)))\r
+      (multiple-value-bind (h-int h-frac) (truncate h-pos)\r
+        (declare (fixnum h-int))\r
+        (let* ((fs (/ s 255))\r
+               (fv (/ v 255))\r
+               (p (round (* 255 fv (- 1 fs))))\r
+               (q (round (* 255 fv (- 1 (* fs h-frac)))))\r
+               (t_ (round (* 255 fv (- 1 (* fs (- 1 h-frac))))))\r
+               r g b)\r
+          \r
+          (cond\r
+           ((zerop h-int)\r
+            (setf r v\r
+                  g t_  \r
+                  b p))\r
+           ((= 1 h-int)\r
+            (setf r q\r
+                  g v\r
+                  b p))\r
+           ((= 2 h-int)\r
+            (setf r p\r
+                  g v\r
+                  b t_))\r
+           ((= 3 h-int)\r
+            (setf r p\r
+                  g q\r
+                  b v))\r
+           ((= 4 h-int)\r
+            (setf r t_\r
+                  g p\r
+                  b v))\r
+           ((= 5 h-int)\r
+            (setf r v\r
+                  g p\r
+                  b q)))\r
+          (values r g b))))))\r
 \r
+\r
+\r
+(defun rgb->hsv (r g b)\r
+  (declare (optimize (speed 3) (safety 0)))\r
+  \r
   (let* ((min (min r g b))\r
          (max (max r g b))\r
          (delta (- max min))\r
          (v max)\r
          (s 0)\r
-         (h 0))\r
+         (h nil))\r
 \r
     (when (plusp max)\r
       (setq s (/ delta max)))\r
 \r
     (when (plusp delta)\r
-      (when (and (= max r) (/= max g))\r
-        (incf h (/ (- g b) delta)))\r
-      (when (and (= max g) (/= max b))\r
-        (incf h (+ 2 (/ (- b r) delta))))\r
-      (when (and (= max b) (/= max r))\r
-        (incf h (+ 4 (/ (- r g) delta))))\r
-      (setq h (* 60 h)))\r
+      (setq h (cond\r
+               ((= max r)\r
+                (nth-value 0 (/ (- g b) delta)))\r
+               ((= max g)\r
+                (nth-value 0 (+ 2 (/ (- b r) delta))))\r
+               (t\r
+                (nth-value 0 (+ 4 (/ (- r g) delta))))))\r
+      (setq h (the fixnum (* 60 h)))\r
+      (when (minusp h)\r
+        (incf h 360)))\r
+    \r
+    (values h s v)))\r
 \r
+(defun rgb255->hsv255 (r g b)\r
+  "Integer convert from rgb from 0-255 -> h from 0-360 and sv from 0-255"\r
+  (declare (fixnum r g b)\r
+           (optimize (speed 3) (safety 0) (space 0) (compilation-speed 0)))\r
+\r
+  (let* ((min (min r g b))\r
+         (max (max r g b))\r
+         (delta (- max min))\r
+         (v max)\r
+         (s 0)\r
+         (h nil))\r
+    (declare (fixnum min max delta v s)\r
+             (type (or null fixnum) h))\r
+    \r
+    (when (plusp max)\r
+      (setq s (truncate (the fixnum (* 255 delta)) max)))\r
+\r
+    (when (plusp delta)\r
+      (setq h (cond\r
+               ((= max r)\r
+                (truncate (the fixnum (* 60 (the fixnum (- g b)))) delta))\r
+               ((= max g)\r
+                (the fixnum\r
+                     (+ 120 (truncate (the fixnum (* 60 (the fixnum (- b r)))) delta))))\r
+               (t\r
+                (the fixnum\r
+                     (+ 240 (truncate (the fixnum (* 60 (the fixnum (- r g)))) delta))))))\r
+      (when (minusp h)\r
+        (incf h 360)))\r
+    \r
     (values h s v)))\r
 \r
+\r
+(defun hsv-equal (h1 s1 v1 h2 s2 v2 &key (limit .001))\r
+  (declare (optimize (speed 3) (safety 0) (space 0) (compilation-speed 0)))\r
+  (flet ((~= (a b)\r
+           (cond \r
+            ((and (null a) (null b))\r
+             t)\r
+            ((or (null a) (null b))\r
+             nil)\r
+            (t\r
+             (< (abs (- a b)) limit)))))\r
+    (cond\r
+     ((and (~= 0 v1) (~= 0 v2))\r
+      t)\r
+     ((or (null h1) (null h2))\r
+      (when (and (~= 0 s1) (~= 0 s2) (~= v1 v2))\r
+        t))\r
+     (t\r
+      (when (~= h1 h2) (~= s1 s2) (~= v1 v2)\r
+        t)))))\r
+\r
+(defun hsv255-equal (h1 s1 v1 h2 s2 v2 &key (limit 1))\r
+  (declare (type fixnum s1 v1 s2 v2 limit)\r
+           (type (or null fixnum) h1 h2)\r
+           (optimize (speed 3) (safety 0) (space 0) (compilation-speed 0)))\r
+  (flet ((~= (a b)\r
+           (declare (type (or null fixnum) a b))\r
+           (cond \r
+            ((and (null a) (null b))\r
+             t)\r
+            ((or (null a) (null b))\r
+             nil)\r
+            (t\r
+             (<= (abs (the fixnum (- a b))) limit)))))\r
+    (cond\r
+     ((and (~= 0 v1) (~= 0 v2))\r
+      t)\r
+     ((or (null h1) (null h2))\r
+      (when (and (~= 0 s1) (~= 0 s2) (~= v1 v2))\r
+        t))\r
+     (t\r
+      (when (~= h1 h2) (~= s1 s2) (~= v1 v2)\r
+        t)))))\r
+\r
+(defun hsv-similar (h1 s1 v1 h2 s2 v2 &key \r
+                       (hue-range 15) (value-range .2) (saturation-range 0.2)\r
+                       (gray-limit 0.3) (black-limit 0.3))\r
+  "Returns T if two HSV values are similar."\r
+  (cond\r
+   ;; all black colors are similar\r
+   ((and (<= v1 black-limit) (<= v2 black-limit))\r
+    t)\r
+   ;; all desaturated (gray) colors are similar for a value, despite hue\r
+   ((and (<= s1 gray-limit) (<= s2 gray-limit))\r
+    (when (<= (abs (- v1 v2)) value-range)\r
+      t))\r
+   (t\r
+    (when (and (<= (abs (hue-difference h1 h2)) hue-range)\r
+               (<= (abs (- v1 v2)) value-range)\r
+               (<= (abs (- s1 s2)) saturation-range))\r
+      t))))\r
+\r
+\r
+(defun hsv255-similar (h1 s1 v1 h2 s2 v2 \r
+                          &key (hue-range 15) (value-range 50) (saturation-range 50)\r
+                          (gray-limit 75) (black-limit 75))\r
+  "Returns T if two HSV values are similar."\r
+  (declare (fixnum s1 v1 s2 v2 hue-range value-range saturation-range\r
+                   gray-limit black-limit)\r
+           (type (or null fixnum) h1 h2)\r
+           (optimize (speed 3) (safety 0) (space 0) (compilation-speed 0)))\r
+  (cond\r
+   ;; all black colors are similar\r
+   ((and (<= v1 black-limit) (<= v2 black-limit))\r
+    t)\r
+   ;; all desaturated (gray) colors are similar for a value, despite hue\r
+   ((and (<= s1 gray-limit) (<= s2 gray-limit))\r
+    (when (<= (abs (- v1 v2)) value-range)\r
+      t))\r
+   (t\r
+    (when (and (<= (abs (hue-difference-fixnum h1 h2)) hue-range)\r
+               (<= (abs (- v1 v2)) value-range)\r
+               (<= (abs (- s1 s2)) saturation-range))\r
+      t))))\r
+\r
+\r
+   \r
+(defun hue-difference (h1 h2)\r
+  "Return difference between two hues around 360 degree circle"\r
+  (cond\r
+   ((and (null h1) (null h2))\r
+    t)\r
+   ((or (null h1) (null h2))\r
+    360)\r
+   (t\r
+    (let ((diff (- h2 h1)))\r
+      (cond\r
+       ((< diff -180)\r
+        (+ 360 diff)\r
+        )\r
+       ((> diff 180)\r
+        (- (- 360 diff)))\r
+       (t\r
+        diff))))))\r
\r
+  \r
+(defun hue-difference-fixnum (h1 h2)\r
+  "Return difference between two hues around 360 degree circle"\r
+  (cond\r
+   ((and (null h1) (null h2))\r
+    t)\r
+   ((or (null h1) (null h2))\r
+    360)\r
+   (t\r
+    (locally (declare (type fixnum h1 h2))\r
+      (let ((diff (- h2 h1)))\r
+        (cond\r
+         ((< diff -180)\r
+          (+ 360 diff)\r
+          )\r
+         ((> diff 180)\r
+          (- (- 360 diff)))\r
+         (t\r
+          diff)))))))\r
  \r