diff --git a/commands/issue.go b/commands/issue.go index a42e0d729af752ddc35500116afe6e0f7812cc00..4e8199d2666a2fa5be780a41f463a58c1d148110 100644 --- a/commands/issue.go +++ b/commands/issue.go @@ -595,8 +595,9 @@ func formatLabel(label github.IssueLabel, colorize bool) string { } func colorizeLabel(label github.IssueLabel, color *utils.Color) string { - return fmt.Sprintf("\033[38;5;%d;48;2;%d;%d;%dm %s \033[m", - getSuitableLabelTextColor(color), color.Red, color.Green, color.Blue, label.Name) + bgColorCode := utils.RgbToTermColorCode(color) + return fmt.Sprintf("\033[38;5;%d;48;%sm %s \033[m", + getSuitableLabelTextColor(color), bgColorCode, label.Name) } func getSuitableLabelTextColor(color *utils.Color) int { diff --git a/utils/color.go b/utils/color.go new file mode 100644 index 0000000000000000000000000000000000000000..3a84419c6306c41e2ee4b8a1e9cc17234ed3c42d --- /dev/null +++ b/utils/color.go @@ -0,0 +1,113 @@ +package utils + +import ( + "fmt" + "math" + "os" + "strconv" +) + +func init() { + initColorCube() +} + +type Color struct { + Red int64 + Green int64 + Blue int64 +} + +func NewColor(hex string) (*Color, error) { + red, err := strconv.ParseInt(hex[0:2], 16, 16) + if err != nil { + return nil, err + } + green, err := strconv.ParseInt(hex[2:4], 16, 16) + if err != nil { + return nil, err + } + blue, err := strconv.ParseInt(hex[4:6], 16, 16) + if err != nil { + return nil, err + } + + return &Color{ + Red: red, + Green: green, + Blue: blue, + }, nil +} + +func (c *Color) Brightness() float32 { + return (0.299*float32(c.Red) + + 0.587*float32(c.Green) + + 0.114*float32(c.Blue)) / 255 +} + +func (c *Color) Distance(other *Color) float64 { + return math.Sqrt(float64(math.Pow(float64(c.Red-other.Red), 2) + + math.Pow(float64(c.Green-other.Green), 2) + + math.Pow(float64(c.Blue-other.Blue), 2))) +} + +var x6colorIndexes = [6]int64{0, 95, 135, 175, 215, 255} +var x6colorCube [216]Color + +func initColorCube() { + i := 0 + for iR := 0; iR < 6; iR++ { + for iG := 0; iG < 6; iG++ { + for iB := 0; iB < 6; iB++ { + x6colorCube[i] = Color{ + x6colorIndexes[iR], + x6colorIndexes[iG], + x6colorIndexes[iB], + } + i++ + } + } + } +} + +func ditherTo256ColorCode(color *Color) (code int) { + iMatch := -1 + minDistance := float64(99999) + for i := 0; i < 216; i++ { + distance := color.Distance(&x6colorCube[i]) + if distance < minDistance { + iMatch = i + minDistance = distance + } + } + return iMatch + 16 +} + +var non24bitColorTerms = []string{ + "Apple_Terminal", +} +var isTerm24bitColorCapableCache bool +var isTerm24bitColorCapableCacheIsInit bool = false + +func isTerm24bitColorCapable() bool { + if !isTerm24bitColorCapableCacheIsInit { + isTerm24bitColorCapableCache = true + myTermProg := os.Getenv("TERM_PROGRAM") + for _, brokenTerm := range non24bitColorTerms { + if myTermProg == brokenTerm { + isTerm24bitColorCapableCache = false + break + } + } + isTerm24bitColorCapableCacheIsInit = true + } + return isTerm24bitColorCapableCache +} + +func RgbToTermColorCode(color *Color) string { + if isTerm24bitColorCapable() { + return fmt.Sprintf("2;%d;%d;%d", color.Red, color.Green, color.Blue) + } else { + intCode := ditherTo256ColorCode(color) + return fmt.Sprintf("5;%d", intCode) + } +} diff --git a/utils/color_test.go b/utils/color_test.go new file mode 100644 index 0000000000000000000000000000000000000000..2022285dffb17b00ad81f77298b75f8c69e42a82 --- /dev/null +++ b/utils/color_test.go @@ -0,0 +1,13 @@ +package utils + +import ( + "github.com/bmizerany/assert" + "testing" +) + +func TestColorBrightness(t *testing.T) { + c, err := NewColor("880000") + assert.Equal(t, nil, err) + actual := c.Brightness() + assert.Equal(t, float32(0.15946665406227112), actual) +} diff --git a/utils/utils.go b/utils/utils.go index 8c2b906249e531de68f317e2b173541041ec490c..3501751b8e07bdf09326858bf8d3fa0b0d243d2b 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -7,7 +7,6 @@ import ( "os/exec" "path/filepath" "runtime" - "strconv" "strings" "time" @@ -83,37 +82,6 @@ func IsOption(confirm, short, long string) bool { return strings.EqualFold(confirm, short) || strings.EqualFold(confirm, long) } -type Color struct { - Red int64 - Green int64 - Blue int64 -} - -func NewColor(hex string) (*Color, error) { - red, err := strconv.ParseInt(hex[0:2], 16, 16) - if err != nil { - return nil, err - } - green, err := strconv.ParseInt(hex[2:4], 16, 16) - if err != nil { - return nil, err - } - blue, err := strconv.ParseInt(hex[4:6], 16, 16) - if err != nil { - return nil, err - } - - return &Color{ - Red: red, - Green: green, - Blue: blue, - }, nil -} - -func (c *Color) Brightness() float32 { - return (0.299*float32(c.Red) + 0.587*float32(c.Green) + 0.114*float32(c.Blue)) / 255 -} - func TimeAgo(t time.Time) string { duration := timeNow().Sub(t) minutes := duration.Minutes() diff --git a/utils/utils_test.go b/utils/utils_test.go index 78e3e3ce20ea5bc6ca80a1ec48019fa26a624bc8..869c59632e1b5e7bc0958410143eee00213a6689 100644 --- a/utils/utils_test.go +++ b/utils/utils_test.go @@ -69,10 +69,3 @@ func TestTimeAgo(t *testing.T) { actual = TimeAgo(yearsAgo) assert.Equal(t, "2 years ago", actual) } - -func TestColorBrightness(t *testing.T) { - c, err := NewColor("880000") - assert.Equal(t, nil, err) - actual := c.Brightness() - assert.Equal(t, float32(0.15946665406227112), actual) -}