|
|
@ -8,6 +8,7 @@ package models |
|
|
|
import ( |
|
|
|
import ( |
|
|
|
"fmt" |
|
|
|
"fmt" |
|
|
|
"html/template" |
|
|
|
"html/template" |
|
|
|
|
|
|
|
"math" |
|
|
|
"regexp" |
|
|
|
"regexp" |
|
|
|
"strconv" |
|
|
|
"strconv" |
|
|
|
"strings" |
|
|
|
"strings" |
|
|
@ -138,19 +139,44 @@ func (label *Label) BelongsToRepo() bool { |
|
|
|
return label.RepoID > 0 |
|
|
|
return label.RepoID > 0 |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// SrgbToLinear converts a component of an sRGB color to its linear intensity
|
|
|
|
|
|
|
|
// See: https://en.wikipedia.org/wiki/SRGB#The_reverse_transformation_(sRGB_to_CIE_XYZ)
|
|
|
|
|
|
|
|
func SrgbToLinear(color uint8) float64 { |
|
|
|
|
|
|
|
flt := float64(color) / 255 |
|
|
|
|
|
|
|
if flt <= 0.04045 { |
|
|
|
|
|
|
|
return flt / 12.92 |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return math.Pow((flt+0.055)/1.055, 2.4) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Luminance returns the luminance of an sRGB color
|
|
|
|
|
|
|
|
func Luminance(color uint32) float64 { |
|
|
|
|
|
|
|
r := SrgbToLinear(uint8(0xFF & (color >> 16))) |
|
|
|
|
|
|
|
g := SrgbToLinear(uint8(0xFF & (color >> 8))) |
|
|
|
|
|
|
|
b := SrgbToLinear(uint8(0xFF & color)) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// luminance ratios for sRGB
|
|
|
|
|
|
|
|
return 0.2126*r + 0.7152*g + 0.0722*b |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// LuminanceThreshold is the luminance at which white and black appear to have the same contrast
|
|
|
|
|
|
|
|
// i.e. x such that 1.05 / (x + 0.05) = (x + 0.05) / 0.05
|
|
|
|
|
|
|
|
// i.e. math.Sqrt(1.05*0.05) - 0.05
|
|
|
|
|
|
|
|
const LuminanceThreshold float64 = 0.179 |
|
|
|
|
|
|
|
|
|
|
|
// ForegroundColor calculates the text color for labels based
|
|
|
|
// ForegroundColor calculates the text color for labels based
|
|
|
|
// on their background color.
|
|
|
|
// on their background color.
|
|
|
|
func (label *Label) ForegroundColor() template.CSS { |
|
|
|
func (label *Label) ForegroundColor() template.CSS { |
|
|
|
if strings.HasPrefix(label.Color, "#") { |
|
|
|
if strings.HasPrefix(label.Color, "#") { |
|
|
|
if color, err := strconv.ParseUint(label.Color[1:], 16, 64); err == nil { |
|
|
|
if color, err := strconv.ParseUint(label.Color[1:], 16, 64); err == nil { |
|
|
|
r := float32(0xFF & (color >> 16)) |
|
|
|
// NOTE: see web_src/js/components/ContextPopup.vue for similar implementation
|
|
|
|
g := float32(0xFF & (color >> 8)) |
|
|
|
luminance := Luminance(uint32(color)) |
|
|
|
b := float32(0xFF & color) |
|
|
|
|
|
|
|
luminance := (0.2126*r + 0.7152*g + 0.0722*b) / 255 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if luminance < 0.66 { |
|
|
|
// prefer white or black based upon contrast
|
|
|
|
|
|
|
|
if luminance < LuminanceThreshold { |
|
|
|
return template.CSS("#fff") |
|
|
|
return template.CSS("#fff") |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return template.CSS("#000") |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|