Klimi's new dotfiles with stow.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

124 lines
4.5 KiB

4 years ago
  1. ;;; colir.el --- Color blending library -*- lexical-binding: t -*-
  2. ;; Copyright (C) 2015-2019 Free Software Foundation, Inc.
  3. ;; Author: Oleh Krehel <ohwoeowho@gmail.com>
  4. ;; This file is part of GNU Emacs.
  5. ;; This file is free software; you can redistribute it and/or modify
  6. ;; it under the terms of the GNU General Public License as published by
  7. ;; the Free Software Foundation; either version 3, or (at your option)
  8. ;; any later version.
  9. ;; This program is distributed in the hope that it will be useful,
  10. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. ;; GNU General Public License for more details.
  13. ;; For a full copy of the GNU General Public License
  14. ;; see <https://www.gnu.org/licenses/>.
  15. ;;; Commentary:
  16. ;; This package solves the problem of adding a face with a background
  17. ;; to text which may already have a background. In all conflicting
  18. ;; areas, instead of choosing either the original or the new
  19. ;; background face, their blended sum is used.
  20. ;;
  21. ;; The blend mode functions are taken from URL
  22. ;; `https://en.wikipedia.org/wiki/Blend_modes'.
  23. ;;; Code:
  24. (require 'cl-lib)
  25. (require 'color)
  26. (defcustom colir-compose-method #'colir-compose-alpha
  27. "Select a method to compose two color channels."
  28. :group 'ivy
  29. :type '(radio
  30. (function-item colir-compose-alpha)
  31. (function-item colir-compose-overlay)
  32. (function-item colir-compose-soft-light)))
  33. (defun colir-compose-soft-light (a b)
  34. "Compose A and B channels."
  35. (if (< b 0.5)
  36. (+ (* 2 a b) (* a a (- 1 b b)))
  37. (+ (* 2 a (- 1 b)) (* (sqrt a) (- (* 2 b) 1)))))
  38. (defun colir-compose-overlay (a b)
  39. "Compose A and B channels."
  40. (if (< a 0.5)
  41. (* 2 a b)
  42. (- 1 (* 2 (- 1 a) (- 1 b)))))
  43. (defun colir-compose-alpha (a b &optional alpha gamma)
  44. "Compose A and B channels.
  45. Optional argument ALPHA is a number between 0.0 and 1.0 which corresponds
  46. to the influence of A on the result. Default value is 0.5.
  47. Optional argument GAMMA is used for gamma correction. Default value is 2.2."
  48. (setq alpha (or alpha 0.5))
  49. (setq gamma (or gamma 2.2))
  50. (+ (* (expt a gamma) alpha) (* (expt b gamma) (- 1 alpha))))
  51. (defun colir-blend (c1 c2)
  52. "Blend the two colors C1 and C2 using `colir-compose-method'.
  53. C1 and C2 are triples of floats in [0.0 1.0] range."
  54. (apply #'color-rgb-to-hex
  55. (cl-mapcar
  56. (if (eq (frame-parameter nil 'background-mode) 'dark)
  57. ;; this method works nicely for dark themes
  58. 'colir-compose-soft-light
  59. colir-compose-method)
  60. c1 c2)))
  61. (defun colir-color-parse (color)
  62. "Convert string COLOR to triple of floats in [0.0 1.0]."
  63. (if (string-match "#\\([[:xdigit:]]\\{2\\}\\)\\([[:xdigit:]]\\{2\\}\\)\\([[:xdigit:]]\\{2\\}\\)" color)
  64. (mapcar (lambda (v) (/ (string-to-number v 16) 255.0))
  65. (list (match-string 1 color) (match-string 2 color) (match-string 3 color)))
  66. ;; does not work properly in terminal (maps color to nearest color
  67. ;; from available color palette).
  68. (color-name-to-rgb color)))
  69. (defun colir--blend-background (start next prevn face object)
  70. (let ((background-prev (face-background prevn)))
  71. (progn
  72. (put-text-property
  73. start next 'face
  74. (if background-prev
  75. (cons `(background-color
  76. . ,(colir-blend
  77. (colir-color-parse background-prev)
  78. (colir-color-parse (face-background face nil t))))
  79. prevn)
  80. (list face prevn))
  81. object))))
  82. (defun colir-blend-face-background (start end face &optional object)
  83. "Append to the face property of the text from START to END the face FACE.
  84. When the text already has a face with a non-plain background,
  85. blend it with the background of FACE.
  86. Optional argument OBJECT is the string or buffer containing the text.
  87. See also `font-lock-append-text-property'."
  88. (let (next prev prevn)
  89. (while (/= start end)
  90. (setq next (next-single-property-change start 'face object end))
  91. (setq prev (get-text-property start 'face object))
  92. (setq prevn (if (listp prev)
  93. (cl-find-if #'atom prev)
  94. prev))
  95. (cond
  96. ((or (keywordp (car-safe prev)) (consp (car-safe prev)))
  97. (put-text-property start next 'face (cons face prev) object))
  98. ((facep prevn)
  99. (colir--blend-background start next prevn face object))
  100. (t
  101. (put-text-property start next 'face face object)))
  102. (setq start next))))
  103. (provide 'colir)
  104. ;;; colir.el ends here