提交 1c2c69ae 编写于 作者: M Mislav Marohnić

Simplify picking contrasting label text color

Pick between white and black depending on which one is the first to
satisfy the contrast ratio of 7.0 (or 4.5 as fallback).
上级 d0310e9c
......@@ -606,38 +606,32 @@ func colorizeLabel(label github.IssueLabel, color *utils.Color) string {
fgColorCode, bgColorCode, label.Name)
}
type contrastCandidate struct {
color *utils.Color
contrast float64
}
func pickHighContrastTextColor(color *utils.Color) *utils.Color {
var candidates [12]*utils.Color
hsl := color.ToHsl()
a := hsl.ScaleLightness(0.5)
candidates[0] = a.ToRgb()
candidates[1] = a.ScaleLightness(0.25).ToRgb()
candidates[2] = a.ScaleSaturation(-0.25).ToRgb()
b := hsl.ScaleSaturation(0.75)
candidates[3] = b.ToRgb()
candidates[4] = b.ScaleLightness(0.25).ToRgb()
candidates[5] = b.ScaleSaturation(-0.25).ToRgb()
c := hsl.ScaleLightness(-0.5)
candidates[6] = c.ToRgb()
candidates[7] = c.ScaleLightness(-0.25).ToRgb()
candidates[8] = c.ScaleSaturation(-0.25).ToRgb()
d := hsl.ScaleSaturation(-0.75)
candidates[9] = d.ToRgb()
candidates[10] = d.ScaleLightness(-0.25).ToRgb()
candidates[11] = d.ScaleSaturation(-0.25).ToRgb()
foundContrastRatio := -999.0
ix := -1
for i := 0; i < 12; i++ {
contrastRatio := color.ContrastRatio(candidates[i])
if contrastRatio > foundContrastRatio {
ix = i
foundContrastRatio = contrastRatio
candidates := []contrastCandidate{}
appendCandidate := func(c *utils.Color) {
candidates = append(candidates, contrastCandidate{
color: c,
contrast: color.ContrastRatio(c),
})
}
appendCandidate(utils.White)
appendCandidate(utils.Black)
for _, candidate := range candidates {
if candidate.contrast >= 7.0 {
return candidate.color
}
}
if foundContrastRatio >= 7.0 {
return candidates[ix]
} else {
return candidates[11]
for _, candidate := range candidates {
if candidate.contrast >= 4.5 {
return candidate.color
}
}
return utils.Black
}
......@@ -66,7 +66,7 @@ func TestFormatIssue(t *testing.T) {
},
format: format,
colorize: true,
expect: "\033[32m #42\033[m An issue with labels \033[38;2;64;64;64;48;2;128;0;0m bug \033[m \033[38;2;0;0;0;48;2;85;255;85m reproduced \033[m\n",
expect: "\033[32m #42\033[m An issue with labels \033[38;2;255;255;255;48;2;128;0;0m bug \033[m \033[38;2;0;0;0;48;2;85;255;85m reproduced \033[m\n",
},
{
name: "not colorized",
......@@ -181,7 +181,7 @@ func TestFormatIssue_customFormatString(t *testing.T) {
issue: issue,
format: "%l",
colorize: true,
expect: "\033[38;2;68;68;68;48;2;136;0;0m bug \033[m \033[38;2;68;68;68;48;2;0;136;0m feature \033[m",
expect: "\033[38;2;255;255;255;48;2;136;0;0m bug \033[m \033[38;2;255;255;255;48;2;0;136;0m feature \033[m",
},
{
name: "label not colorized",
......
......@@ -7,8 +7,14 @@ import (
"strconv"
)
var (
Black, White *Color
)
func init() {
initColorCube()
Black, _ = NewColor("000000")
White, _ = NewColor("ffffff")
}
type Color struct {
......@@ -38,12 +44,6 @@ func NewColor(hex string) (*Color, error) {
}, 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) +
......@@ -78,86 +78,6 @@ func (c *Color) ContrastRatio(other *Color) float64 {
return ratio
}
type HslColor struct {
Hue float64
Saturation float64
Lightness float64
}
func (c *Color) ToHsl() *HslColor {
rPrime := float64(c.Red) / 255
gPrime := float64(c.Green) / 255
bPrime := float64(c.Blue) / 255
cMax := math.Max(rPrime, math.Max(gPrime, bPrime))
cMin := math.Min(rPrime, math.Min(gPrime, bPrime))
delta := cMax - cMin
var H float64
if delta == 0 {
H = 0
} else if cMax == rPrime {
H = 60 * math.Mod((gPrime-bPrime)/delta, 6)
} else if cMax == gPrime {
H = 60 * (((bPrime - rPrime) / delta) + 2)
} else {
H = 60 * (((rPrime - gPrime) / delta) + 4)
}
var L float64
L = (cMax + cMin) / 2
var S float64
if delta == 0 {
S = 0
} else {
S = delta / (1 - math.Abs((2*L)-1))
}
return &HslColor{H, S, L}
}
func (c *HslColor) ToRgb() *Color {
C := (1 - math.Abs(2*c.Lightness-1)) * c.Saturation
X := C * (1 - math.Abs(math.Mod(c.Hue/60, 2)-1))
m := c.Lightness - (C / 2)
var rPrime, gPrime, bPrime float64
switch {
case c.Hue < 60.0:
rPrime, gPrime, bPrime = C, X, 0
case c.Hue < 120.0:
rPrime, gPrime, bPrime = X, C, 0
case c.Hue < 180.0:
rPrime, gPrime, bPrime = 0, C, X
case c.Hue < 240.0:
rPrime, gPrime, bPrime = 0, X, C
case c.Hue < 300.0:
rPrime, gPrime, bPrime = X, 0, C
case c.Hue < 360.0:
rPrime, gPrime, bPrime = C, 0, X
}
R := uint8((rPrime + m) * 255)
G := uint8((gPrime + m) * 255)
B := uint8((bPrime + m) * 255)
return &Color{R, G, B}
}
func (c *HslColor) ScaleLightness(x float64) *HslColor {
newLightness := round(math.Min(math.Max(0, c.Lightness*(1.0+x)), 1.0))
return &HslColor{c.Hue, c.Saturation, newLightness}
}
func (c *HslColor) ScaleSaturation(x float64) *HslColor {
newSaturation := round(math.Min(math.Max(0, c.Lightness*(1.0+x)), 1.0))
return &HslColor{c.Hue, newSaturation, c.Lightness}
}
func round(n float64) float64 {
i := math.Trunc(n)
if math.Abs(n-i) >= 0.5 {
return i + math.Copysign(1, n)
}
return i
}
var x6colorIndexes = [6]uint8{0, 95, 135, 175, 215, 255}
var x6colorCube [216]Color
......
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)
}
func TestRoundDown(t *testing.T) {
assert.Equal(t, 3.0, round(3.0))
assert.Equal(t, 3.0, round(3.01))
assert.Equal(t, 3.0, round(3.49))
}
func TestRoundUp(t *testing.T) {
assert.Equal(t, 3.0, round(2.5))
assert.Equal(t, 3.0, round(2.51))
assert.Equal(t, 3.0, round(2.99))
}
func TestRoundNegative(t *testing.T) {
assert.Equal(t, -2.0, round(-2.49))
assert.Equal(t, -3.0, round(-2.5))
assert.Equal(t, -3.0, round(-2.51))
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册