~delthas/senpai-dev

extended color scheme: use rgb colors v1 PROPOSED

Lauri Tirkkonen: 3
 extended color scheme: use rgb colors
 extended color scheme: detect background color & vary lightness
 extended color scheme: detect background color & vary lightness

 5 files changed, 85 insertions(+), 52 deletions(-)
Export patchset (mbox)
How do I use this?

Copy & paste the following snippet into your terminal to import this patchset into git:

curl -s https://lists.sr.ht/~delthas/senpai-dev/patches/55950/mbox | git am -3
Learn more about email & git

[PATCH] extended color scheme: use rgb colors Export this patch

---
 ui/colors.go | 83 ++++++++++++++++++++++++++++------------------------
 1 file changed, 45 insertions(+), 38 deletions(-)

diff --git a/ui/colors.go b/ui/colors.go
index 096bdbe..f356234 100644
--- a/ui/colors.go
+++ b/ui/colors.go
@@ -1,6 +1,7 @@
package ui

import (
	"math"
	"hash/fnv"

	"git.sr.ht/~rockorager/vaxis"
@@ -40,53 +41,59 @@ var colors = map[ColorSchemeType][]vaxis.Color{
		vaxis.IndexColor(13),
		vaxis.IndexColor(14),
	},
	// all XTerm extended colors with HSL saturation=1, light=0.5
	ColorSchemeExtended: {
		vaxis.IndexColor(196), // HSL hue: 0°
		vaxis.IndexColor(202), // HSL hue: 22°
		vaxis.IndexColor(208), // HSL hue: 32°
		vaxis.IndexColor(214), // HSL hue: 41°
		vaxis.IndexColor(220), // HSL hue: 51°
		vaxis.IndexColor(226), // HSL hue: 60°
		vaxis.IndexColor(190), // HSL hue: 69°
		vaxis.IndexColor(154), // HSL hue: 79°
		vaxis.IndexColor(118), // HSL hue: 88°
		vaxis.IndexColor(82),  // HSL hue: 98°
		vaxis.IndexColor(46),  // HSL hue: 120°
		vaxis.IndexColor(47),  // HSL hue: 142°
		vaxis.IndexColor(48),  // HSL hue: 152°
		vaxis.IndexColor(49),  // HSL hue: 161°
		vaxis.IndexColor(50),  // HSL hue: 171°
		vaxis.IndexColor(51),  // HSL hue: 180°
		vaxis.IndexColor(45),  // HSL hue: 189°
		vaxis.IndexColor(39),  // HSL hue: 199°
		vaxis.IndexColor(33),  // HSL hue: 208°
		vaxis.IndexColor(27),  // HSL hue: 218°
		vaxis.IndexColor(21),  // HSL hue: 240°
		vaxis.IndexColor(57),  // HSL hue: 262°
		vaxis.IndexColor(93),  // HSL hue: 272°
		vaxis.IndexColor(129), // HSL hue: 281°
		vaxis.IndexColor(165), // HSL hue: 291°
		vaxis.IndexColor(201), // HSL hue: 300°
		vaxis.IndexColor(200), // HSL hue: 309°
		vaxis.IndexColor(199), // HSL hue: 319°
		vaxis.IndexColor(198), // HSL hue: 328°
		vaxis.IndexColor(197), // HSL hue: 338°
	},
}

func u32ToAngle(n uint32) float64 {
	return float64(n) / float64(math.MaxUint32) * 360
}

func hslToRGB(hue, sat, light float64) (r, g, b uint8) {
	var r1, g1, b1 float64
	chroma := (1 - math.Abs(2 * light - 1)) * sat
	h6 := hue/60
	x := chroma * (1 - math.Abs(math.Mod(h6, 2) - 1))
	if 0 <= h6 && h6 < 1 {
		r1, g1, b1 = chroma, x, 0
	} else if 1 <= h6 && h6 < 2 {
		r1, g1, b1 = x, chroma, 0
	} else if 2 <= h6 && h6 < 3 {
		r1, g1, b1 = 0, chroma, x
	} else if 3 <= h6 && h6 < 4 {
		r1, g1, b1 = 0, x, chroma
	} else if 4 <= h6 && h6 < 5 {
		r1, g1, b1 = x, 0, chroma
	} else if 5 <= h6 && h6 < 6 {
		r1, g1, b1 = chroma, 0, x
	}
	m := light - chroma/2
	r = uint8(math.MaxUint8 * (r1 + m))
	g = uint8(math.MaxUint8 * (g1 + m))
	b = uint8(math.MaxUint8 * (b1 + m))
	return r, g, b
}

func IdentColor(scheme ColorScheme, ident string, self bool) vaxis.Color {
	h := fnv.New32()
	_, _ = h.Write([]byte(ident))
	if scheme.Type == ColorSchemeFixed {
	hash := fnv.New32()
	_, _ = hash.Write([]byte(ident))
	switch scheme.Type {
	case ColorSchemeFixed:
		if self {
			return scheme.Self
		} else {
			return scheme.Others
		}
	case ColorSchemeBase:
		c := colors[scheme.Type]
		return c[int(hash.Sum32()%uint32(len(c)))]
	case ColorSchemeExtended:
		hue := u32ToAngle(hash.Sum32())
		sat := 1.0
		light := 0.5
		return vaxis.RGBColor(hslToRGB(hue, sat, light))

	default:
		panic("invalid color scheme setting")
	}
	c := colors[scheme.Type]
	return c[int(h.Sum32()%uint32(len(c)))]
}

func IdentString(scheme ColorScheme, ident string, self bool) StyledString {
-- 
2.46.2


-- 
Lauri Tirkkonen | lotheac @ IRCnet

[PATCH 2/2] extended color scheme: detect background color & vary lightness Export this patch

humans can detect lightness differences pretty well, so it makes sense
to vary color selection in that dimension too.
---
 app.go       |  8 ++++----
 ui/colors.go | 27 ++++++++++++++++++++-------
 ui/ui.go     | 15 ++++++++++++++-
 3 files changed, 38 insertions(+), 12 deletions(-)

diff --git a/app.go b/app.go
index 2de6d95..91530d3 100644
--- a/app.go
+++ b/app.go
@@ -1994,12 +1994,12 @@ func (app *App) formatMessage(s *irc.Session, ev irc.MessageEvent) (buffer strin
	if isAction || isNotice {
		head = "*"
	} else {
		headColor = ui.IdentColor(app.cfg.Colors.Nicks, head, isFromSelf)
		headColor = app.win.IdentColor(app.cfg.Colors.Nicks, head, isFromSelf)
	}

	var body ui.StyledStringBuilder
	if isNotice {
		color := ui.IdentColor(app.cfg.Colors.Nicks, ev.User, isFromSelf)
		color := app.win.IdentColor(app.cfg.Colors.Nicks, ev.User, isFromSelf)
		body.SetStyle(vaxis.Style{
			Foreground: color,
		})
@@ -2008,7 +2008,7 @@ func (app *App) formatMessage(s *irc.Session, ev irc.MessageEvent) (buffer strin
		body.WriteString(": ")
		body.WriteStyledString(ui.IRCString(content))
	} else if isAction {
		color := ui.IdentColor(app.cfg.Colors.Nicks, ev.User, isFromSelf)
		color := app.win.IdentColor(app.cfg.Colors.Nicks, ev.User, isFromSelf)
		body.SetStyle(vaxis.Style{
			Foreground: color,
		})
@@ -2203,7 +2203,7 @@ func (app *App) updatePrompt() {
		},
		)
	} else {
		prompt = ui.IdentString(app.cfg.Colors.Nicks, s.Nick(), true)
		prompt = app.win.IdentString(app.cfg.Colors.Nicks, s.Nick(), true)
	}
	app.win.SetPrompt(prompt)
}
diff --git a/ui/colors.go b/ui/colors.go
index f356234..b99762b 100644
--- a/ui/colors.go
+++ b/ui/colors.go
@@ -43,8 +43,14 @@ var colors = map[ColorSchemeType][]vaxis.Color{
	},
}

func u32ToAngle(n uint32) float64 {
	return float64(n) / float64(math.MaxUint32) * 360
func mapToRange(n uint16, min1, max1 uint16, min2, max2 float64) float64 {
	// scale val to range [0,1]
	val := float64(n - min1) / float64(max1 - min1)
	return min2 + (max2-min2) * val
}

func u16ToAngle(n uint16) float64 {
	return float64(n) / float64(math.MaxUint16) * 360
}

func hslToRGB(hue, sat, light float64) (r, g, b uint8) {
@@ -72,7 +78,7 @@ func hslToRGB(hue, sat, light float64) (r, g, b uint8) {
	return r, g, b
}

func IdentColor(scheme ColorScheme, ident string, self bool) vaxis.Color {
func (ui UI) IdentColor(scheme ColorScheme, ident string, self bool) vaxis.Color {
	hash := fnv.New32()
	_, _ = hash.Write([]byte(ident))
	switch scheme.Type {
@@ -86,9 +92,16 @@ func IdentColor(scheme ColorScheme, ident string, self bool) vaxis.Color {
		c := colors[scheme.Type]
		return c[int(hash.Sum32()%uint32(len(c)))]
	case ColorSchemeExtended:
		hue := u32ToAngle(hash.Sum32())
		sum := hash.Sum32()
		hue := mapToRange(uint16(sum), 0, math.MaxUint16, 0.0, 360.0)
		sat := 1.0
		light := 0.5
		var light float64
		switch ui.colorThemeMode {
		case vaxis.DarkMode:
			light = mapToRange(uint16(sum >> 16), 0, math.MaxUint16, 0.6, 1.0)
		case vaxis.LightMode:
			light = mapToRange(uint16(sum >> 16), 0, math.MaxUint16, 0.0, 0.4)
		}
		return vaxis.RGBColor(hslToRGB(hue, sat, light))

	default:
@@ -96,8 +109,8 @@ func IdentColor(scheme ColorScheme, ident string, self bool) vaxis.Color {
	}
}

func IdentString(scheme ColorScheme, ident string, self bool) StyledString {
	color := IdentColor(scheme, ident, self)
func (ui UI) IdentString(scheme ColorScheme, ident string, self bool) StyledString {
	color := ui.IdentColor(scheme, ident, self)
	style := vaxis.Style{
		Foreground: color,
	}
diff --git a/ui/ui.go b/ui/ui.go
index 7cef99e..b6d0926 100644
--- a/ui/ui.go
+++ b/ui/ui.go
@@ -3,6 +3,7 @@ package ui
import (
	"fmt"
	"image"
	"math"
	"strings"
	"sync/atomic"
	"time"
@@ -75,6 +76,8 @@ type UI struct {
	image vaxis.Image

	mouseLinks bool

	colorThemeMode vaxis.ColorThemeMode
}

func New(config Config) (ui *UI, err error) {
@@ -102,6 +105,16 @@ func New(config Config) (ui *UI, err error) {
		window: vx.Window(),
	}

	bgcol := ui.vx.QueryBackground()
	parm := bgcol.Params()
	r,g,b := float64(parm[0]),float64(parm[1]),float64(parm[2])
	bglight := (math.Max(math.Max(r,g),b) + math.Min(math.Min(r,g),b))/2
	if (bglight > 127) {
		ui.colorThemeMode = vaxis.LightMode
	} else {
		ui.colorThemeMode = vaxis.DarkMode
	}

	ui.vx.SetTitle("senpai")
	ui.vx.SetAppID("senpai")

@@ -847,7 +860,7 @@ func (ui *UI) drawVerticalMemberList(vx *Vaxis, x0, y0, width, height int, membe
				Attribute:  attr,
			})
		} else {
			color := IdentColor(ui.config.Colors.Nicks, m.Name.Name, m.Self)
			color := ui.IdentColor(ui.config.Colors.Nicks, m.Name.Name, m.Self)
			name = Styled(nameText, vaxis.Style{
				Foreground: color,
				Attribute:  attr,
-- 
2.46.2


-- 
Lauri Tirkkonen | lotheac @ IRCnet

Re: [PATCH 2/2] extended color scheme: detect background color & vary lightness Export this patch

I think this actually shows pretty well why this is needed: I adjusted
the max/min lightness values a little bit, and the difference is
quite staggering. My initial values were off; I don't think they should
necessarily be configurable, but they can make a big difference.

diff --git a/ui/colors.go b/ui/colors.go
index b99762b..7fe0b1f 100644
--- a/ui/colors.go
+++ b/ui/colors.go
@@ -98,9 +98,9 @@ func (ui UI) IdentColor(scheme ColorScheme, ident string, self bool) vaxis.Color
		var light float64
		switch ui.colorThemeMode {
		case vaxis.DarkMode:
			light = mapToRange(uint16(sum >> 16), 0, math.MaxUint16, 0.6, 1.0)
			light = mapToRange(uint16(sum >> 16), 0, math.MaxUint16, 0.4, 0.9)
		case vaxis.LightMode:
			light = mapToRange(uint16(sum >> 16), 0, math.MaxUint16, 0.0, 0.4)
			light = mapToRange(uint16(sum >> 16), 0, math.MaxUint16, 0.1, 0.6)
		}
		return vaxis.RGBColor(hslToRGB(hue, sat, light))


-- 
Lauri Tirkkonen | lotheac @ IRCnet