xiwal

Generate terminal color schemes
git clone https://git.ce9e.org/xiwal.git

commit
f74eedfd94f8f578547b141bdb332414a84ff5e8
parent
2e5962262b552e32d1aa991901a35ef39cfdacdc
Author
Tobias Bengfort <tobias.bengfort@posteo.de>
Date
2019-11-15 07:51
split into different files

Diffstat

A wal/__init__.py 0
A wal/__main__.py 19 +++++++++++++++++++
A wal/image.py 17 +++++++++++++++++
R xiwal.py -> wal/lch.py 111 +++++--------------------------------------------------------
A wal/scheme.py 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A wal/term.py 19 +++++++++++++++++++

6 files changed, 125 insertions, 103 deletions


diff --git a/wal/__init__.py b/wal/__init__.py

diff --git a/wal/__main__.py b/wal/__main__.py

@@ -0,0 +1,19 @@
   -1     1 import sys
   -1     2 
   -1     3 from . import image
   -1     4 from . import scheme
   -1     5 from . import term
   -1     6 
   -1     7 
   -1     8 solarized = ['#002b36', '#eee8d5', '#b58900', '#cb4b16', '#dc322f', '#d33682', '#6c71c4', '#268bd2', '#2aa198', '#859900']
   -1     9 gruvbox = ['#3c3836', '#cc241d', '#98971a', '#d79921', '#458588', '#b16286', '#689d6a', '#d65d0e']
   -1    10 tango = ['#555753', '#ef2929', '#8ae234', '#fce94f', '#739fcf', '#ad7fa8', '#34e2e2']
   -1    11 
   -1    12 
   -1    13 if __name__ == '__main__':
   -1    14 	colors = list(image.extract_colors(sys.argv[1], 9))
   -1    15 	# colors = solarized
   -1    16 	scheme = scheme.colors2scheme(colors)
   -1    17 	print(';'.join(scheme))
   -1    18 	term.palette()
   -1    19 	term.apply(scheme)

diff --git a/wal/image.py b/wal/image.py

@@ -0,0 +1,17 @@
   -1     1 import re
   -1     2 import subprocess
   -1     3 
   -1     4 
   -1     5 def extract_colors(path, colors):
   -1     6 	cmd = [
   -1     7 		'convert', path,
   -1     8 		'-alpha', 'deactivate',
   -1     9 		'-colors', str(colors),
   -1    10 		'-unique-colors',
   -1    11 		'txt:-'
   -1    12 	]
   -1    13 	output = subprocess.check_output(cmd)
   -1    14 	for line in output.splitlines()[1:]:
   -1    15 		line = line.decode('ascii')
   -1    16 		match = re.search(r'#[0-9A-F]{6}', line)
   -1    17 		yield match.group()

diff --git a/xiwal.py b/wal/lch.py

@@ -1,11 +1,6 @@
    1    -1 import glob
    2     1 import math
    3    -1 import re
    4    -1 import subprocess
    5    -1 import sys
    6     2 
    7     3 WHITE = (95.05, 100, 108.9)
    8    -1 C_BG = 60
    9     4 
   10     5 
   11     6 def _srgb2rgb(c):
@@ -88,7 +83,7 @@ def _lch2rgb(lch):
   88    83 
   89    84 
   90    85 def lch2rgb(lch):
   91    -1 	rgb = _lch2rgb(lch);
   -1    86 	rgb = _lch2rgb(lch)
   92    87 
   93    88 	if any(x < 0 or x > 255 for x in rgb):
   94    89 		c_min = 0
@@ -96,7 +91,7 @@ def lch2rgb(lch):
   96    91 
   97    92 		while c_max - c_min > 0.01:
   98    93 			c_tmp = (c_min + c_max) / 2
   99    -1 			rgb = _lch2rgb((lch[0], c_tmp, lch[2]));
   -1    94 			rgb = _lch2rgb((lch[0], c_tmp, lch[2]))
  100    95 			if any(x < 0 or x > 255 for x in rgb):
  101    96 				c_max = c_tmp
  102    97 			else:
@@ -105,110 +100,20 @@ def lch2rgb(lch):
  105   100 	return rgb
  106   101 
  107   102 
  108    -1 def chex(rgb):
   -1   103 def format_hex(rgb):
  109   104 	return '#{:02x}{:02x}{:02x}'.format(*[int(x) for x in rgb])
  110   105 
  111   106 
  112    -1 def im(img, colors=9):
  113    -1 	cmd = ['convert', img, '-alpha', 'deactivate', '-colors', str(colors), '-unique-colors', 'txt:-']
  114    -1 	output = subprocess.check_output(cmd)
  115    -1 	for line in output.splitlines()[1:]:
  116    -1 		line = line.decode('ascii')
  117    -1 		match = re.search('srgb\(([0-9]+),([0-9]+),([0-9]+)\)', line)
  118    -1 		color = [int(i, 10) for i in match.groups()]
  119    -1 		yield color
  120    -1 
  121    -1 
  122    -1 def permutate(a, n):
  123    -1 	if n == 0:
  124    -1 		yield ()
  125    -1 	else:
  126    -1 		for i in range(len(a)):
  127    -1 			for rest in permutate(a[:i] + a[i + 1:], n - 1):
  128    -1 				yield (a[i], *rest)
  129    -1 
  130    -1 
  131    -1 def distance(color, i, offset=math.pi * 2 / 15):
  132    -1 	hue = math.pi / 3 * i + offset
  133    -1 	d = abs(color[2] - hue)
  134    -1 	c = color[1]
  135    -1 	if i in [0, 1]:
  136    -1 		c = max(c, C_BG)
  137    -1 	if d > math.pi:
  138    -1 		d = 2 * math.pi - d
  139    -1 	return d ** 4 * c
  140    -1 
  141    -1 
  142    -1 def score(colors):
  143    -1 	s = sum(distance(c, i) for i, c in enumerate(colors))
  144    -1 	s /= sum(c[1] for c in colors)
  145    -1 	return s
  146    -1 
  147    -1 
  148    -1 def scheme(colors, dominant):
  149    -1 	l_dark = 45, 50, 50, 45, 45, 50
  150    -1 	l_light = 60, 70, 80, 60, 60, 75
  151    -1 	c_grey = min(dominant[1], 8)
  152    -1 
  153    -1 	yield 2, c_grey, dominant[2]
  154    -1 	for i in range(6):
  155    -1 		c = colors[i][1] * 1.2
  156    -1 		if i in [0, 1]:
  157    -1 			c = max(c, C_BG)
  158    -1 		yield l_dark[i], c, colors[i][2]
  159    -1 	yield 85, c_grey, dominant[2]
  160    -1 
  161    -1 	yield 20, c_grey, dominant[2]
  162    -1 	for i in range(6):
  163    -1 		c = colors[i][1] * 1.2
  164    -1 		if i in [0, 1]:
  165    -1 			c = max(c, C_BG)
  166    -1 		yield l_light[i], c, colors[i][2]
  167    -1 	yield 100, c_grey, dominant[2]
  168    -1 
  169    -1 
  170    -1 def colors2scheme(colors):
  171    -1 	colors = [rgb2lch(c) for c in colors]
  172    -1 	dominant = colors[0]
  173    -1 	colors = min(permutate(colors, 6), key=score)
  174    -1 	colors = [colors[i] for i in [0, 2, 1, 4, 5, 3]]
  175    -1 	return [chex(lch2rgb(c)) for c in scheme(colors, dominant)]
  176    -1 
  177    -1 
  178    -1 def apply(scheme):
  179    -1 	for path in glob.glob('/dev/pts/[0-9]*'):
  180    -1 		with open(path, 'w') as tty:
  181    -1 			for i in range(0, 16):
  182    -1 				tty.write('\033]4;%s;%s\033\\' % (i, scheme[i]))
  183    -1 			tty.write('\033]%s;%s\033\\' % (11, scheme[0]))
  184    -1 			tty.write('\033]%s;%s\033\\' % (10, scheme[15]))
  185    -1 
  186    -1 
  187    -1 def palette(scheme):
  188    -1 	s = []
  189    -1 	for i in range(0, 16):
  190    -1 		a = '8;5;%s' % i if i > 7 else i
  191    -1 		s.append('\033[4%sm%s\033[0m' % (a, '   '))
  192    -1 	print(''.join(s[:8]))
  193    -1 	print(''.join(s[8:]))
  194    -1 
  195    -1 
  196   107 def parse_hex(s):
  197   108 	r = int(s[1:3], 16)
  198   109 	g = int(s[3:5], 16)
  199   110 	b = int(s[5:7], 16)
  200   111 	return r, g, b
  201   112 
  202    -1 solarized = ['#002b36', '#eee8d5', '#b58900', '#cb4b16', '#dc322f', '#d33682', '#6c71c4', '#268bd2', '#2aa198', '#859900']
  203    -1 gruvbox = ['#3c3836', '#cc241d', '#98971a', '#d79921', '#458588', '#b16286', '#689d6a', '#d65d0e']
   -1   113 
   -1   114 def from_hex(s):
   -1   115 	return rgb2lch(parse_hex(s))
  204   116 
  205   117 
  206    -1 if __name__ == '__main__':
  207    -1 	# foo
  208    -1 	colors = list(im(sys.argv[1]))
  209    -1 	# colors = [parse_hex(c) for c in gruvbox]
  210    -1 	subprocess.call(['chafa', '-s', '40', sys.argv[1]])
  211    -1 	scheme = colors2scheme(colors)
  212    -1 	print(';'.join(scheme))
  213    -1 	palette(scheme)
  214    -1 	apply(scheme)
   -1   118 def to_hex(lch):
   -1   119 	return format_hex(lch2rgb(lch))

diff --git a/wal/scheme.py b/wal/scheme.py

@@ -0,0 +1,62 @@
   -1     1 import math
   -1     2 
   -1     3 from . import lch
   -1     4 
   -1     5 C_BG = 60
   -1     6 L_DARK = 45, 50, 50, 45, 45, 50
   -1     7 L_LIGHT = 60, 70, 80, 60, 60, 75
   -1     8 OFFSET = math.pi * 2 / 15
   -1     9 
   -1    10 
   -1    11 def permutate(a, n):
   -1    12 	if n == 0:
   -1    13 		yield ()
   -1    14 	else:
   -1    15 		for i in range(len(a)):
   -1    16 			for rest in permutate(a[:i] + a[i + 1:], n - 1):
   -1    17 				yield (a[i], *rest)
   -1    18 
   -1    19 
   -1    20 def distance(color, i):
   -1    21 	hue = math.pi / 3 * i + OFFSET
   -1    22 	d = abs(color[2] - hue)
   -1    23 	c = color[1]
   -1    24 	if i in [0, 1]:
   -1    25 		c = max(c, C_BG)
   -1    26 	if d > math.pi:
   -1    27 		d = 2 * math.pi - d
   -1    28 	return d ** 4 * c
   -1    29 
   -1    30 
   -1    31 def score(colors):
   -1    32 	s = sum(distance(c, i) for i, c in enumerate(colors))
   -1    33 	s /= sum(c[1] for c in colors)
   -1    34 	return s
   -1    35 
   -1    36 
   -1    37 def scheme(colors, dominant):
   -1    38 	c_grey = min(dominant[1], 8)
   -1    39 
   -1    40 	yield 2, c_grey, dominant[2]
   -1    41 	for i in range(6):
   -1    42 		c = colors[i][1] * 1.2
   -1    43 		if i in [0, 1]:
   -1    44 			c = max(c, C_BG)
   -1    45 		yield L_DARK[i], c, colors[i][2]
   -1    46 	yield 85, c_grey, dominant[2]
   -1    47 
   -1    48 	yield 20, c_grey, dominant[2]
   -1    49 	for i in range(6):
   -1    50 		c = colors[i][1] * 1.2
   -1    51 		if i in [0, 1]:
   -1    52 			c = max(c, C_BG)
   -1    53 		yield L_LIGHT[i], c, colors[i][2]
   -1    54 	yield 100, c_grey, dominant[2]
   -1    55 
   -1    56 
   -1    57 def colors2scheme(colors):
   -1    58 	colors = [lch.from_hex(c) for c in colors]
   -1    59 	dominant = colors[0]
   -1    60 	colors = min(permutate(colors, 6), key=score)
   -1    61 	colors = [colors[i] for i in [0, 2, 1, 4, 5, 3]]
   -1    62 	return [lch.to_hex(c) for c in scheme(colors, dominant)]

diff --git a/wal/term.py b/wal/term.py

@@ -0,0 +1,19 @@
   -1     1 import glob
   -1     2 
   -1     3 
   -1     4 def apply(scheme):
   -1     5 	for path in glob.glob('/dev/pts/[0-9]*'):
   -1     6 		with open(path, 'w') as tty:
   -1     7 			for i in range(0, 16):
   -1     8 				tty.write('\033]4;%s;%s\033\\' % (i, scheme[i]))
   -1     9 			tty.write('\033]%s;%s\033\\' % (11, scheme[0]))
   -1    10 			tty.write('\033]%s;%s\033\\' % (10, scheme[15]))
   -1    11 
   -1    12 
   -1    13 def palette():
   -1    14 	s = []
   -1    15 	for i in range(0, 16):
   -1    16 		a = '8;5;%s' % i if i > 7 else i
   -1    17 		s.append('\033[4%sm%s\033[0m' % (a, '   '))
   -1    18 	print(''.join(s[:8]))
   -1    19 	print(''.join(s[8:]))