2018-12-22 12:42:36 +03:00
|
|
|
package utils
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"math"
|
|
|
|
"os"
|
|
|
|
"strconv"
|
|
|
|
)
|
|
|
|
|
2019-01-03 06:34:10 +03:00
|
|
|
var (
|
2020-04-13 03:31:50 +03:00
|
|
|
Black *Color
|
|
|
|
White *Color
|
2019-01-03 06:34:10 +03:00
|
|
|
)
|
|
|
|
|
2018-12-22 12:42:36 +03:00
|
|
|
func init() {
|
|
|
|
initColorCube()
|
2019-01-03 06:34:10 +03:00
|
|
|
Black, _ = NewColor("000000")
|
|
|
|
White, _ = NewColor("ffffff")
|
2018-12-22 12:42:36 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
type Color struct {
|
2018-12-22 20:09:54 +03:00
|
|
|
Red uint8
|
|
|
|
Green uint8
|
|
|
|
Blue uint8
|
2018-12-22 12:42:36 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func NewColor(hex string) (*Color, error) {
|
2018-12-22 20:09:54 +03:00
|
|
|
red, err := strconv.ParseUint(hex[0:2], 16, 8)
|
2018-12-22 12:42:36 +03:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2018-12-22 20:09:54 +03:00
|
|
|
green, err := strconv.ParseUint(hex[2:4], 16, 8)
|
2018-12-22 12:42:36 +03:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2018-12-22 20:09:54 +03:00
|
|
|
blue, err := strconv.ParseUint(hex[4:6], 16, 8)
|
2018-12-22 12:42:36 +03:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &Color{
|
2018-12-22 20:09:54 +03:00
|
|
|
Red: uint8(red),
|
|
|
|
Green: uint8(green),
|
|
|
|
Blue: uint8(blue),
|
2018-12-22 12:42:36 +03:00
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Color) Distance(other *Color) float64 {
|
2019-07-23 10:36:19 +03:00
|
|
|
return math.Sqrt(math.Pow(float64(c.Red-other.Red), 2) +
|
2018-12-22 12:42:36 +03:00
|
|
|
math.Pow(float64(c.Green-other.Green), 2) +
|
2019-07-23 10:36:19 +03:00
|
|
|
math.Pow(float64(c.Blue-other.Blue), 2))
|
2018-12-22 12:42:36 +03:00
|
|
|
}
|
|
|
|
|
2018-12-22 20:09:54 +03:00
|
|
|
func rgbComponentToBoldValue(component uint8) float64 {
|
2018-12-22 19:09:46 +03:00
|
|
|
srgb := float64(component) / 255
|
|
|
|
if srgb <= 0.03928 {
|
|
|
|
return srgb / 12.92
|
|
|
|
}
|
2020-04-13 03:31:50 +03:00
|
|
|
return math.Pow(((srgb + 0.055) / 1.055), 2.4)
|
2018-12-22 19:09:46 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Color) Luminance() float64 {
|
|
|
|
return 0.2126*rgbComponentToBoldValue(c.Red) +
|
|
|
|
0.7152*rgbComponentToBoldValue(c.Green) +
|
|
|
|
0.0722*rgbComponentToBoldValue(c.Blue)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Color) ContrastRatio(other *Color) float64 {
|
|
|
|
L := c.Luminance()
|
|
|
|
otherL := other.Luminance()
|
|
|
|
var L1, L2 float64
|
|
|
|
if L > otherL {
|
|
|
|
L1, L2 = L, otherL
|
|
|
|
} else {
|
|
|
|
L1, L2 = otherL, L
|
|
|
|
}
|
|
|
|
ratio := (L1 + 0.05) / (L2 + 0.05)
|
|
|
|
return ratio
|
|
|
|
}
|
|
|
|
|
2018-12-22 20:09:54 +03:00
|
|
|
var x6colorIndexes = [6]uint8{0, 95, 135, 175, 215, 255}
|
2018-12-22 12:42:36 +03:00
|
|
|
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)
|
|
|
|
}
|
2020-04-13 03:31:50 +03:00
|
|
|
intCode := ditherTo256ColorCode(color)
|
|
|
|
return fmt.Sprintf("5;%d", intCode)
|
2018-12-22 12:42:36 +03:00
|
|
|
}
|