1package utils 2 3import ( 4 "fmt" 5 "math" 6 "os" 7 "strconv" 8) 9 10var ( 11 Black, White *Color 12) 13 14func init() { 15 initColorCube() 16 Black, _ = NewColor("000000") 17 White, _ = NewColor("ffffff") 18} 19 20type Color struct { 21 Red uint8 22 Green uint8 23 Blue uint8 24} 25 26func NewColor(hex string) (*Color, error) { 27 red, err := strconv.ParseUint(hex[0:2], 16, 8) 28 if err != nil { 29 return nil, err 30 } 31 green, err := strconv.ParseUint(hex[2:4], 16, 8) 32 if err != nil { 33 return nil, err 34 } 35 blue, err := strconv.ParseUint(hex[4:6], 16, 8) 36 if err != nil { 37 return nil, err 38 } 39 40 return &Color{ 41 Red: uint8(red), 42 Green: uint8(green), 43 Blue: uint8(blue), 44 }, nil 45} 46 47func (c *Color) Distance(other *Color) float64 { 48 return math.Sqrt(math.Pow(float64(c.Red-other.Red), 2) + 49 math.Pow(float64(c.Green-other.Green), 2) + 50 math.Pow(float64(c.Blue-other.Blue), 2)) 51} 52 53func rgbComponentToBoldValue(component uint8) float64 { 54 srgb := float64(component) / 255 55 if srgb <= 0.03928 { 56 return srgb / 12.92 57 } else { 58 return math.Pow(((srgb + 0.055) / 1.055), 2.4) 59 } 60} 61 62func (c *Color) Luminance() float64 { 63 return 0.2126*rgbComponentToBoldValue(c.Red) + 64 0.7152*rgbComponentToBoldValue(c.Green) + 65 0.0722*rgbComponentToBoldValue(c.Blue) 66} 67 68func (c *Color) ContrastRatio(other *Color) float64 { 69 L := c.Luminance() 70 otherL := other.Luminance() 71 var L1, L2 float64 72 if L > otherL { 73 L1, L2 = L, otherL 74 } else { 75 L1, L2 = otherL, L 76 } 77 ratio := (L1 + 0.05) / (L2 + 0.05) 78 return ratio 79} 80 81var x6colorIndexes = [6]uint8{0, 95, 135, 175, 215, 255} 82var x6colorCube [216]Color 83 84func initColorCube() { 85 i := 0 86 for iR := 0; iR < 6; iR++ { 87 for iG := 0; iG < 6; iG++ { 88 for iB := 0; iB < 6; iB++ { 89 x6colorCube[i] = Color{ 90 x6colorIndexes[iR], 91 x6colorIndexes[iG], 92 x6colorIndexes[iB], 93 } 94 i++ 95 } 96 } 97 } 98} 99 100func ditherTo256ColorCode(color *Color) (code int) { 101 iMatch := -1 102 minDistance := float64(99999) 103 for i := 0; i < 216; i++ { 104 distance := color.Distance(&x6colorCube[i]) 105 if distance < minDistance { 106 iMatch = i 107 minDistance = distance 108 } 109 } 110 return iMatch + 16 111} 112 113var non24bitColorTerms = []string{ 114 "Apple_Terminal", 115} 116var isTerm24bitColorCapableCache bool 117var isTerm24bitColorCapableCacheIsInit bool = false 118 119func isTerm24bitColorCapable() bool { 120 if !isTerm24bitColorCapableCacheIsInit { 121 isTerm24bitColorCapableCache = true 122 myTermProg := os.Getenv("TERM_PROGRAM") 123 for _, brokenTerm := range non24bitColorTerms { 124 if myTermProg == brokenTerm { 125 isTerm24bitColorCapableCache = false 126 break 127 } 128 } 129 isTerm24bitColorCapableCacheIsInit = true 130 } 131 return isTerm24bitColorCapableCache 132} 133 134func RgbToTermColorCode(color *Color) string { 135 if isTerm24bitColorCapable() { 136 return fmt.Sprintf("2;%d;%d;%d", color.Red, color.Green, color.Blue) 137 } else { 138 intCode := ditherTo256ColorCode(color) 139 return fmt.Sprintf("5;%d", intCode) 140 } 141} 142