0

When defining a new face, how to blend/interpolate between two existing faces colors?

For example - mix the colors foreground of default and the background of the error face with a factor 0.0 being all of the first color, 1.0 returning all of the second.

5
  • 2
    Please clarify in the question what you mean by blending face colors, i.e., what you're requesting. As it is, you posed an unclear question and then answered it, providing real understanding of what you were requesting only in the answer (the code). The question should describe what you want (better).
    – Drew
    Oct 23 '21 at 16:47
  • Updated the question.
    – ideasman42
    Oct 24 '21 at 5:03
  • 1
    What do you mean by "blend"?
    – NickD
    Oct 24 '21 at 13:31
  • I don't think there's a built in function for this. The closest I see is color-gradient which can construct multiple in-between steps. For example if you wanted a color 0.1 from the foreground / 0.9 from the background you could call (color-gradient foreground background 9) and it would return the 9 in-between colors at 0.1, 0.2, …, 0.9.
    – amitp
    Oct 25 '21 at 4:25
  • This issue with gradient is it creates a list of colors, which is overkill when only one color is needed.
    – ideasman42
    Oct 25 '21 at 4:31
1

This macro blends colors between faces, example:

(defun my-face-color-blend (color-a color-b factor)
  "Tint between COLOR-A and COLOR-B by FACTOR in (0..1).
Use for mixing face colors."
  (let ((value-a (color-values color-a))
        (value-b (color-values color-b))
        (ifactor (- 1.0 factor)))
    (apply 'format
           (cons
            "#%02x%02x%02x"
            (mapcar
             (lambda (n)
               ;; Shift by -8 to map the value returned by `color values':
               ;; 0..65535 to 0..255 for `#RRGGBB` string formatting.
               (ash (truncate (+ (* (nth n value-a) ifactor)
                                 (* (nth n value-b) factor))) -8))
             (number-sequence 0 2))))))

(defface my-half-background-face
  (list (list t (list :foreground
                      (my-face-color-blend
                       (face-attribute 'default :foreground)
                       (face-attribute 'error   :background)
                       0.5))))
  "Half the background and foreground of the default font.")
6
  • Any reason this needs to be a macro rather than a function?
    – Dan
    Oct 23 '21 at 23:18
  • Good point, it should be a function (done).
    – ideasman42
    Oct 24 '21 at 7:07
  • I'd let-bind color-a and color-b inside the function, and use different names from the parameters. And after staring at it for a couple of minutes, I still don't understand the shift. Part of it might be that I don't understand what you mean by "blend" (as I asked in my comment to your question), so I'm left with reverse-engineering the code to figure that out.
    – NickD
    Oct 24 '21 at 17:39
  • 1
    Use let binding, clarified blend in the OP.
    – ideasman42
    Oct 25 '21 at 4:31
  • 1
    Added an explanation for why the shift is there.
    – ideasman42
    Oct 25 '21 at 7:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.