aboutsummaryrefslogtreecommitdiff
path: root/color
diff options
context:
space:
mode:
Diffstat (limited to 'color')
-rw-r--r--color/color.go649
1 files changed, 649 insertions, 0 deletions
diff --git a/color/color.go b/color/color.go
new file mode 100644
index 0000000..a3cbd16
--- /dev/null
+++ b/color/color.go
@@ -0,0 +1,649 @@
+package color
+
+import (
+ "fmt"
+ "math"
+)
+
+const (
+ epsilon float64 = 216.0 / 24389.0
+ kappa float64 = 24389.0 / 27.0
+)
+
+var (
+ // reference white in XYZ coordinates
+ // цветовые координаты излучателя белого света в пространстве XYZ
+ whitePoints = map[string][3]float64{
+ "A": {1.09850, 1.0, 0.35585},
+ "B": {0.99072, 1.0, 0.85223},
+ "C": {0.98074, 1.0, 1.18232},
+ "D50": {0.96422, 1.0, 0.82521},
+ "D55": {0.95682, 1.0, 0.92149},
+ "D65": {0.95047, 1.0, 1.08883},
+ "D75": {0.94972, 1.0, 1.22638},
+ "E": {1.00000, 1.0, 1.00000},
+ "F2": {0.99186, 1.0, 0.67393},
+ "F7": {0.95041, 1.0, 1.08747},
+ "F11": {1.00962, 1.0, 0.64350},
+ }
+
+ // sRGB to XYZ conversion matrix
+ convertMatrix = [3][3]float64{
+ {0.4124564, 0.3575761, 0.1804375},
+ {0.2126729, 0.7151522, 0.0721750},
+ {0.0193339, 0.1191920, 0.9503041},
+ }
+
+ // XYZ to sRGB conversion matrix
+ inverseMatrix = [3][3]float64{
+ {3.2404542, -1.5371385, -0.4985314},
+ {-0.9692660, 1.8760108, 0.0415560},
+ {0.0556434, -0.2040259, 1.0572252},
+ }
+)
+
+func sqr(v float64) float64 {
+ return v * v
+}
+
+type RGBColor struct {
+ R, G, B int // RGB-компоненты [0..255]
+}
+
+func (rgb RGBColor) String() string {
+ return fmt.Sprintf("RGB:{%d %d %d}", rgb.R, rgb.G, rgb.B)
+}
+
+func (rgb RGBColor) RGB() RGBColor {
+ return rgb
+}
+
+func (rgb RGBColor) XYZ() XYZColor {
+
+ f := func(n float64) float64 {
+ if n > 0.04045 {
+ // math.Pow(base, exponent) = math.Exp(math.Log(base) * exponent)
+ //return math.Exp(2.4 * math.Log((n+0.055)/1.055))
+ return math.Pow(((n + 0.055) / 1.055), 2.4)
+ } else {
+ return n / 12.92
+ }
+ }
+
+ r := f(float64(rgb.R) / 255.0)
+ g := f(float64(rgb.G) / 255.0)
+ b := f(float64(rgb.B) / 255.0)
+
+ //
+ // [X] [Xr Xg Xb] [R]
+ // [Y] = [Yr Yg Yb] x [G]
+ // [Z] [Zr Zg Zb] [B]
+ //
+ //Observer. = 2°, Illuminant = D65
+ return XYZColor{
+ r*0.4124 + g*0.3576 + b*0.1805,
+ r*0.2126 + g*0.7152 + b*0.0722,
+ r*0.0193 + g*0.1192 + b*0.9505,
+ }
+}
+
+func (rgb RGBColor) Lab() LabColor {
+ return rgb.XYZ().Lab()
+}
+
+type XYZColor struct {
+ X, Y, Z float64
+}
+
+func (xyz XYZColor) String() string {
+ return fmt.Sprintf("XYZ:{%4.7f %4.7f %4.7f}", xyz.X, xyz.Y, xyz.Z)
+}
+
+func (xyz XYZColor) RGB() RGBColor {
+ // TODO!!!
+ return RGBColor{0, 0, 0}
+}
+
+func (xyz XYZColor) XYZ() XYZColor {
+ return xyz
+}
+
+// ok
+func (xyz XYZColor) Lab() LabColor {
+
+ f := func(n float64) float64 {
+ if n > epsilon {
+ return math.Pow(n, 1.0/3.0)
+ } else {
+ return (kappa*n + 16.0) / 116.0
+ }
+ }
+
+ // Observer= 2°, Illuminant= D65
+ whitePoint := whitePoints["D65"]
+
+ x := f(xyz.X / whitePoint[0])
+ y := f(xyz.Y / whitePoint[1])
+ z := f(xyz.Z / whitePoint[2])
+
+ return LabColor{
+ (116.0 * y) - 16.0,
+ 500.0 * (x - y),
+ 200.0 * (y - z),
+ }
+}
+
+type LabColor struct {
+ L, A, B float64
+}
+
+func (lab LabColor) String() string {
+ return fmt.Sprintf("Lab:{%4.7f %4.7f %4.7f}", lab.L, lab.A, lab.B)
+}
+
+func (lab LabColor) RGB() RGBColor {
+ return lab.XYZ().RGB()
+}
+
+// ok
+func (lab LabColor) XYZ() XYZColor {
+
+ f := func(n float64) float64 {
+ if n3 := n * n * n; n3 > epsilon {
+ return n3
+ } else {
+ return (n*116.0 - 16.0) / kappa
+ }
+ }
+
+ y := (lab.L + 16.0) / 116.0
+ x := 0.002*lab.A + y
+ z := y - 0.005*lab.B
+
+ // Observer= 2°, Illuminant= D65
+ whitePoint := whitePoints["D65"]
+
+ return XYZColor{
+ f(x) * whitePoint[0],
+ f(y) * whitePoint[1],
+ f(z) * whitePoint[2],
+ }
+}
+
+func (lab LabColor) Lab() LabColor {
+ return lab
+}
+
+// ok
+func (lab LabColor) LCh() LChColor {
+ c := math.Sqrt(sqr(lab.A) + sqr(lab.B))
+ h := 180.0 * math.Atan2(lab.B, lab.A) / math.Pi
+ if h < 0.0 {
+ h += 360.0
+ }
+ return LChColor{lab.L, c, h}
+}
+
+type LChColor struct {
+ L, C, H float64
+}
+
+func (lch LChColor) RGB() RGBColor {
+ return lch.Lab().XYZ().RGB()
+}
+
+func (lch LChColor) XYZ() XYZColor {
+ return lch.Lab().XYZ()
+}
+
+// ok
+func (lch LChColor) Lab() LabColor {
+ a := lch.C * math.Cos(lch.H*math.Pi/180.0)
+ b := lch.C * math.Sin(lch.H*math.Pi/180.0)
+ return LabColor{lch.L, a, b}
+}
+
+type Colorer interface {
+ RGB() RGBColor
+ XYZ() XYZColor
+ Lab() LabColor
+}
+
+type Comparer interface {
+ Compare(c1, c2 Colorer) float64
+}
+
+type DeltaCIE76 struct{}
+
+// DeltaCIE76.Compare computes the CIE76 color difference.
+// This is just Euclidean distance in Lab space, and therefore quite fast,
+// though it exhibits perceptual uniformity issues especially in the blue and desaturated regions.
+func (DeltaCIE76) Compare(c1, c2 Colorer) float64 {
+ lab1 := c1.Lab()
+ lab2 := c2.Lab()
+ return math.Sqrt(sqr(lab1.L-lab2.L) + sqr(lab1.A-lab2.A) + sqr(lab1.B-lab2.B))
+}
+
+type DeltaCIE94 struct{}
+
+// KLCh94 is a struct for weighting factors for CIE94 ΔE calculation.
+type KLCh94 struct {
+ KL, KC, Kh, K1, K2 float64
+}
+
+// KLCH94GraphicArts are the weighting factors for CIE94 used for most uses except textiles.
+var KLCH94GraphicArts = KLCh94{1, 1, 1, 0.045, 0.015}
+
+// KLCH94Textiles are the weighting factors for CIE94 used for textiles.
+var KLCH94Textiles = KLCh94{2, 1, 1, 0.048, 0.014}
+
+//var KLCH94 = &KLCH94GraphicArts
+
+// DeltaCIE94.Compare computes the CIE94 color difference of two L*a*b* colors.
+// This is a distance calculation with the addition of weighting factors specified by klch.
+func (DeltaCIE94) Compare(c1, c2 Colorer) float64 {
+ lab1 := c1.Lab()
+ lab2 := c2.Lab()
+
+ dL := sqr(lab1.L - lab2.L)
+
+ c1ab := math.Sqrt(sqr(lab1.A) + sqr(lab1.B))
+ c2ab := math.Sqrt(sqr(lab2.A) + sqr(lab2.B))
+
+ dC := sqr(c1ab - c2ab)
+
+ dH := sqr(lab1.A-lab2.A) + sqr(lab1.B-lab2.B) - dC
+
+ KLCH94 := &KLCH94GraphicArts
+
+ sC := 1.0 + KLCH94.K1*c1ab
+ sH := 1.0 + KLCH94.K2*c1ab
+
+ return math.Sqrt(
+ dL/sqr(KLCH94.KL) +
+ dC/sqr(KLCH94.KC*sC) +
+ dH/sqr(KLCH94.Kh*sH))
+}
+
+type DeltaCIE2000 struct{}
+
+type KLCh struct {
+ KL, KC, Kh float64
+}
+
+// KLCHDefault is the most commonly used set of weighting parameters for CIEDE2000
+var KLCH = KLCh{1, 1, 1}
+
+// CIE2000 computes the CIEDE2000 delta-E for two L*a*b* space color coordinates
+// klch is for configuring the weighting factors, but this almost always should be KLCHDefault
+// Note that this implementation will exhibit slightly different behavior around the discontinuities
+// of the function (these are grey colors) compared to Java and most C runtimes. The golang atan
+// function has different accuracy characteristics compared to most Unix platforms and Java Strict math
+func (DeltaCIE2000) Compare(col1, col2 Colorer) float64 {
+ lab1 := col1.Lab()
+ lab2 := col2.Lab()
+
+ lBarPrime := (lab1.L + lab2.L) * 0.5
+ c1 := math.Sqrt(lab1.A*lab1.A + lab1.B*lab1.B)
+ c2 := math.Sqrt(lab2.A*lab2.A + lab2.B*lab2.B)
+ cBar := (c1 + c2) * 0.5
+
+ cBar7 := cBar * cBar * cBar
+ cBar7 *= cBar7 * cBar
+ g := 0.5 * (1.0 - math.Sqrt(cBar7/(cBar7+6103515625.0))) // 25**7
+
+ a1Prime := (1.0 + g) * lab1.A
+ a2Prime := (1.0 + g) * lab2.A
+
+ c1Prime := math.Sqrt(a1Prime*a1Prime + lab1.B*lab1.B)
+ c2Prime := math.Sqrt(a2Prime*a2Prime + lab2.B*lab2.B)
+
+ cBarPrime := (c1Prime + c2Prime) * 0.5
+
+ h1Prime := math.Atan2(lab1.B, a1Prime)
+ if h1Prime < 0 {
+ h1Prime += 2 * math.Pi
+
+ }
+ h2Prime := math.Atan2(lab2.B, a2Prime)
+ if h2Prime < 0 {
+ h2Prime += 2 * math.Pi
+
+ }
+
+ hBarPrime := (h1Prime + h2Prime) * 0.5
+ dhPrime := h2Prime - h1Prime
+ if math.Abs(dhPrime) > math.Pi {
+ hBarPrime += math.Pi
+ if h2Prime <= h1Prime {
+ dhPrime += 2 * math.Pi
+
+ } else {
+ dhPrime -= 2 * math.Pi
+
+ }
+
+ }
+
+ t := 1.0 -
+ 0.17*math.Cos(hBarPrime-math.Pi/6) +
+ 0.24*math.Cos(2.0*hBarPrime) +
+ 0.32*math.Cos(3.0*hBarPrime+math.Pi/30) -
+ 0.20*math.Cos(4.0*hBarPrime-63.0*math.Pi/180)
+
+ dLPrime := lab2.L - lab1.L
+ dCPrime := c2Prime - c1Prime
+ dHPrime := 2.0 * math.Sqrt(c1Prime*c2Prime) * math.Sin(dhPrime/2.0)
+
+ lBarPrimeM50Sqr := lBarPrime - 50.0
+ lBarPrimeM50Sqr *= lBarPrimeM50Sqr
+ sL := 1.0 + (0.015*lBarPrimeM50Sqr)/math.Sqrt(20.0+lBarPrimeM50Sqr)
+ sC := 1.0 + 0.045*cBarPrime
+ sH := 1.0 + 0.015*cBarPrime*t
+
+ hBarPrimeM := (180/math.Pi*hBarPrime - 275.0) / 25.0
+ dTheta := math.Pi / 6 * math.Exp(-hBarPrimeM*hBarPrimeM)
+ cBarPrime7 := cBarPrime * cBarPrime * cBarPrime
+ cBarPrime7 *= cBarPrime7 * cBarPrime
+ rC := math.Sqrt(cBarPrime7 / (cBarPrime7 + 6103515625.0))
+ rT := -2.0 * rC * math.Sin(2.0*dTheta)
+
+ return math.Sqrt(
+ sqr(dLPrime/(KLCH.KL*sL)) +
+ sqr(dCPrime/(KLCH.KC*sC)) +
+ sqr(dHPrime/(KLCH.Kh*sH)) +
+ (dCPrime/(KLCH.KC*sC))*(dHPrime/(KLCH.Kh*sH))*rT)
+
+}
+
+func Approximate(color Colorer, comparer Comparer) (float64, int) {
+ best := 0
+ bestdist := 10000000.0
+ for i, applicant := range XtermLabPalette {
+ if dist := comparer.Compare(color, applicant); dist < bestdist {
+ best, bestdist = i, dist
+ }
+ }
+ return bestdist, best + 17
+}
+
+//color = round(36 * (r * 5) + 6 * (g * 5) + (b * 5) + 16)
+func HackApproximate(color Colorer) int {
+ c := color.RGB()
+ r := float64(c.R / 255)
+ g := float64(c.B / 255)
+ b := float64(c.G / 255)
+ return int(36*(r*5)+6*(g*5)+b*5) + 17
+}
+
+var (
+ /*
+ {
+ {R: 0x00, G: 0x00, B: 0x00}, // "#000000" 16 index of palette
+ {R: 0x00, G: 0x00, B: 0x5F}, // "#00005f"
+ {R: 0x00, G: 0x00, B: 0x87}, // "#000087"
+ {R: 0x00, G: 0x00, B: 0xAF}, // "#0000af"
+ {R: 0x00, G: 0x00, B: 0xD7}, // "#0000d7"
+ {R: 0x00, G: 0x00, B: 0xFF}, // "#0000ff"
+ {R: 0x00, G: 0x5F, B: 0x00}, // "#005f00"
+ {R: 0x00, G: 0x5F, B: 0x5F}, // "#005f5f"
+ {R: 0x00, G: 0x5F, B: 0x87}, // "#005f87"
+ {R: 0x00, G: 0x5F, B: 0xAF}, // "#005faf"
+ {R: 0x00, G: 0x5F, B: 0xD7}, // "#005fd7"
+ {R: 0x00, G: 0x5F, B: 0xFF}, // "#005fff"
+ {R: 0x00, G: 0x87, B: 0x00}, // "#008700"
+ {R: 0x00, G: 0x87, B: 0x5F}, // "#00875f"
+ {R: 0x00, G: 0x87, B: 0x87}, // "#008787"
+ {R: 0x00, G: 0x87, B: 0xAF}, // "#0087af"
+ {R: 0x00, G: 0x87, B: 0xD7}, // "#0087d7"
+ {R: 0x00, G: 0x87, B: 0xFF}, // "#0087ff"
+ {R: 0x00, G: 0xAF, B: 0x00}, // "#00af00"
+ {R: 0x00, G: 0xAF, B: 0x5F}, // "#00af5f"
+ {R: 0x00, G: 0xAF, B: 0x87}, // "#00af87"
+ {R: 0x00, G: 0xAF, B: 0xAF}, // "#00afaf"
+ {R: 0x00, G: 0xAF, B: 0xD7}, // "#00afd7"
+ {R: 0x00, G: 0xAF, B: 0xFF}, // "#00afff"
+ {R: 0x00, G: 0xD7, B: 0x00}, // "#00d700"
+ {R: 0x00, G: 0xD7, B: 0x5F}, // "#00d75f"
+ {R: 0x00, G: 0xD7, B: 0x87}, // "#00d787"
+ {R: 0x00, G: 0xD7, B: 0xAF}, // "#00d7af"
+ {R: 0x00, G: 0xD7, B: 0xD7}, // "#00d7d7"
+ {R: 0x00, G: 0xD7, B: 0xFF}, // "#00d7ff"
+ {R: 0x00, G: 0xFF, B: 0x00}, // "#00ff00"
+ {R: 0x00, G: 0xFF, B: 0x5F}, // "#00ff5f"
+ {R: 0x00, G: 0xFF, B: 0x87}, // "#00ff87"
+ {R: 0x00, G: 0xFF, B: 0xAF}, // "#00ffaf"
+ {R: 0x00, G: 0xFF, B: 0xD7}, // "#00ffd7"
+ {R: 0x00, G: 0xFF, B: 0xFF}, // "#00ffff"
+ {R: 0x5F, G: 0x00, B: 0x00}, // "#5f0000"
+ {R: 0x5F, G: 0x00, B: 0x5F}, // "#5f005f"
+ {R: 0x5F, G: 0x00, B: 0x87}, // "#5f0087"
+ {R: 0x5F, G: 0x00, B: 0xAF}, // "#5f00af"
+ {R: 0x5F, G: 0x00, B: 0xD7}, // "#5f00d7"
+ {R: 0x5F, G: 0x00, B: 0xFF}, // "#5f00ff"
+ {R: 0x5F, G: 0x5F, B: 0x00}, // "#5f5f00"
+ {R: 0x5F, G: 0x5F, B: 0x5F}, // "#5f5f5f"
+ {R: 0x5F, G: 0x5F, B: 0x87}, // "#5f5f87"
+ {R: 0x5F, G: 0x5F, B: 0xAF}, // "#5f5faf"
+ {R: 0x5F, G: 0x5F, B: 0xD7}, // "#5f5fd7"
+ {R: 0x5F, G: 0x5F, B: 0xFF}, // "#5f5fff"
+ {R: 0x5F, G: 0x87, B: 0x00}, // "#5f8700"
+ {R: 0x5F, G: 0x87, B: 0x5F}, // "#5f875f"
+ {R: 0x5F, G: 0x87, B: 0x87}, // "#5f8787"
+ {R: 0x5F, G: 0x87, B: 0xAF}, // "#5f87af"
+ {R: 0x5F, G: 0x87, B: 0xD7}, // "#5f87d7"
+ {R: 0x5F, G: 0x87, B: 0xFF}, // "#5f87ff"
+ {R: 0x5F, G: 0xAF, B: 0x00}, // "#5faf00"
+ {R: 0x5F, G: 0xAF, B: 0x5F}, // "#5faf5f"
+ {R: 0x5F, G: 0xAF, B: 0x87}, // "#5faf87"
+ {R: 0x5F, G: 0xAF, B: 0xAF}, // "#5fafaf"
+ {R: 0x5F, G: 0xAF, B: 0xD7}, // "#5fafd7"
+ {R: 0x5F, G: 0xAF, B: 0xFF}, // "#5fafff"
+ {R: 0x5F, G: 0xD7, B: 0x00}, // "#5fd700"
+ {R: 0x5F, G: 0xD7, B: 0x5F}, // "#5fd75f"
+ {R: 0x5F, G: 0xD7, B: 0x87}, // "#5fd787"
+ {R: 0x5F, G: 0xD7, B: 0xAF}, // "#5fd7af"
+ {R: 0x5F, G: 0xD7, B: 0xD7}, // "#5fd7d7"
+ {R: 0x5F, G: 0xD7, B: 0xFF}, // "#5fd7ff"
+ {R: 0x5F, G: 0xFF, B: 0x00}, // "#5fff00"
+ {R: 0x5F, G: 0xFF, B: 0x5F}, // "#5fff5f"
+ {R: 0x5F, G: 0xFF, B: 0x87}, // "#5fff87"
+ {R: 0x5F, G: 0xFF, B: 0xAF}, // "#5fffaf"
+ {R: 0x5F, G: 0xFF, B: 0xD7}, // "#5fffd7"
+ {R: 0x5F, G: 0xFF, B: 0xFF}, // "#5fffff"
+ {R: 0x87, G: 0x00, B: 0x00}, // "#870000"
+ {R: 0x87, G: 0x00, B: 0x5F}, // "#87005f"
+ {R: 0x87, G: 0x00, B: 0x87}, // "#870087"
+ {R: 0x87, G: 0x00, B: 0xAF}, // "#8700af"
+ {R: 0x87, G: 0x00, B: 0xD7}, // "#8700d7"
+ {R: 0x87, G: 0x00, B: 0xFF}, // "#8700ff"
+ {R: 0x87, G: 0x5F, B: 0x00}, // "#875f00"
+ {R: 0x87, G: 0x5F, B: 0x5F}, // "#875f5f"
+ {R: 0x87, G: 0x5F, B: 0x87}, // "#875f87"
+ {R: 0x87, G: 0x5F, B: 0xAF}, // "#875faf"
+ {R: 0x87, G: 0x5F, B: 0xD7}, // "#875fd7"
+ {R: 0x87, G: 0x5F, B: 0xFF}, // "#875fff"
+ {R: 0x87, G: 0x87, B: 0x00}, // "#878700"
+ {R: 0x87, G: 0x87, B: 0x5F}, // "#87875f"
+ {R: 0x87, G: 0x87, B: 0x87}, // "#878787"
+ {R: 0x87, G: 0x87, B: 0xAF}, // "#8787af"
+ {R: 0x87, G: 0x87, B: 0xD7}, // "#8787d7"
+ {R: 0x87, G: 0x87, B: 0xFF}, // "#8787ff"
+ {R: 0x87, G: 0xAF, B: 0x00}, // "#87af00"
+ {R: 0x87, G: 0xAF, B: 0x5F}, // "#87af5f"
+ {R: 0x87, G: 0xAF, B: 0x87}, // "#87af87"
+ {R: 0x87, G: 0xAF, B: 0xAF}, // "#87afaf"
+ {R: 0x87, G: 0xAF, B: 0xD7}, // "#87afd7"
+ {R: 0x87, G: 0xAF, B: 0xFF}, // "#87afff"
+ {R: 0x87, G: 0xD7, B: 0x00}, // "#87d700"
+ {R: 0x87, G: 0xD7, B: 0x5F}, // "#87d75f"
+ {R: 0x87, G: 0xD7, B: 0x87}, // "#87d787"
+ {R: 0x87, G: 0xD7, B: 0xAF}, // "#87d7af"
+ {R: 0x87, G: 0xD7, B: 0xD7}, // "#87d7d7"
+ {R: 0x87, G: 0xD7, B: 0xFF}, // "#87d7ff"
+ {R: 0x87, G: 0xFF, B: 0x00}, // "#87ff00"
+ {R: 0x87, G: 0xFF, B: 0x5F}, // "#87ff5f"
+ {R: 0x87, G: 0xFF, B: 0x87}, // "#87ff87"
+ {R: 0x87, G: 0xFF, B: 0xAF}, // "#87ffaf"
+ {R: 0x87, G: 0xFF, B: 0xD7}, // "#87ffd7"
+ {R: 0x87, G: 0xFF, B: 0xFF}, // "#87ffff"
+ {R: 0xAF, G: 0x00, B: 0x00}, // "#af0000"
+ {R: 0xAF, G: 0x00, B: 0x5F}, // "#af005f"
+ {R: 0xAF, G: 0x00, B: 0x87}, // "#af0087"
+ {R: 0xAF, G: 0x00, B: 0xAF}, // "#af00af"
+ {R: 0xAF, G: 0x00, B: 0xD7}, // "#af00d7"
+ {R: 0xAF, G: 0x00, B: 0xFF}, // "#af00ff"
+ {R: 0xAF, G: 0x5F, B: 0x00}, // "#af5f00"
+ {R: 0xAF, G: 0x5F, B: 0x5F}, // "#af5f5f"
+ {R: 0xAF, G: 0x5F, B: 0x87}, // "#af5f87"
+ {R: 0xAF, G: 0x5F, B: 0xAF}, // "#af5faf"
+ {R: 0xAF, G: 0x5F, B: 0xD7}, // "#af5fd7"
+ {R: 0xAF, G: 0x5F, B: 0xFF}, // "#af5fff"
+ {R: 0xAF, G: 0x87, B: 0x00}, // "#af8700"
+ {R: 0xAF, G: 0x87, B: 0x5F}, // "#af875f"
+ {R: 0xAF, G: 0x87, B: 0x87}, // "#af8787"
+ {R: 0xAF, G: 0x87, B: 0xAF}, // "#af87af"
+ {R: 0xAF, G: 0x87, B: 0xD7}, // "#af87d7"
+ {R: 0xAF, G: 0x87, B: 0xFF}, // "#af87ff"
+ {R: 0xAF, G: 0xAF, B: 0x00}, // "#afaf00"
+ {R: 0xAF, G: 0xAF, B: 0x5F}, // "#afaf5f"
+ {R: 0xAF, G: 0xAF, B: 0x87}, // "#afaf87"
+ {R: 0xAF, G: 0xAF, B: 0xAF}, // "#afafaf"
+ {R: 0xAF, G: 0xAF, B: 0xD7}, // "#afafd7"
+ {R: 0xAF, G: 0xAF, B: 0xFF}, // "#afafff"
+ {R: 0xAF, G: 0xD7, B: 0x00}, // "#afd700"
+ {R: 0xAF, G: 0xD7, B: 0x5F}, // "#afd75f"
+ {R: 0xAF, G: 0xD7, B: 0x87}, // "#afd787"
+ {R: 0xAF, G: 0xD7, B: 0xAF}, // "#afd7af"
+ {R: 0xAF, G: 0xD7, B: 0xD7}, // "#afd7d7"
+ {R: 0xAF, G: 0xD7, B: 0xFF}, // "#afd7ff"
+ {R: 0xAF, G: 0xFF, B: 0x00}, // "#afff00"
+ {R: 0xAF, G: 0xFF, B: 0x5F}, // "#afff5f"
+ {R: 0xAF, G: 0xFF, B: 0x87}, // "#afff87"
+ {R: 0xAF, G: 0xFF, B: 0xAF}, // "#afffaf"
+ {R: 0xAF, G: 0xFF, B: 0xD7}, // "#afffd7"
+ {R: 0xAF, G: 0xFF, B: 0xFF}, // "#afffff"
+ {R: 0xD7, G: 0x00, B: 0x00}, // "#d70000"
+ {R: 0xD7, G: 0x00, B: 0x5F}, // "#d7005f"
+ {R: 0xD7, G: 0x00, B: 0x87}, // "#d70087"
+ {R: 0xD7, G: 0x00, B: 0xAF}, // "#d700af"
+ {R: 0xD7, G: 0x00, B: 0xD7}, // "#d700d7"
+ {R: 0xD7, G: 0x00, B: 0xFF}, // "#d700ff"
+ {R: 0xD7, G: 0x5F, B: 0x00}, // "#d75f00"
+ {R: 0xD7, G: 0x5F, B: 0x5F}, // "#d75f5f"
+ {R: 0xD7, G: 0x5F, B: 0x87}, // "#d75f87"
+ {R: 0xD7, G: 0x5F, B: 0xAF}, // "#d75faf"
+ {R: 0xD7, G: 0x5F, B: 0xD7}, // "#d75fd7"
+ {R: 0xD7, G: 0x5F, B: 0xFF}, // "#d75fff"
+ {R: 0xD7, G: 0x87, B: 0x00}, // "#d78700"
+ {R: 0xD7, G: 0x87, B: 0x5F}, // "#d7875f"
+ {R: 0xD7, G: 0x87, B: 0x87}, // "#d78787"
+ {R: 0xD7, G: 0x87, B: 0xAF}, // "#d787af"
+ {R: 0xD7, G: 0x87, B: 0xD7}, // "#d787d7"
+ {R: 0xD7, G: 0x87, B: 0xFF}, // "#d787ff"
+ {R: 0xD7, G: 0xAF, B: 0x00}, // "#d7af00"
+ {R: 0xD7, G: 0xAF, B: 0x5F}, // "#d7af5f"
+ {R: 0xD7, G: 0xAF, B: 0x87}, // "#d7af87"
+ {R: 0xD7, G: 0xAF, B: 0xAF}, // "#d7afaf"
+ {R: 0xD7, G: 0xAF, B: 0xD7}, // "#d7afd7"
+ {R: 0xD7, G: 0xAF, B: 0xFF}, // "#d7afff"
+ {R: 0xD7, G: 0xD7, B: 0x00}, // "#d7d700"
+ {R: 0xD7, G: 0xD7, B: 0x5F}, // "#d7d75f"
+ {R: 0xD7, G: 0xD7, B: 0x87}, // "#d7d787"
+ {R: 0xD7, G: 0xD7, B: 0xAF}, // "#d7d7af"
+ {R: 0xD7, G: 0xD7, B: 0xD7}, // "#d7d7d7"
+ {R: 0xD7, G: 0xD7, B: 0xFF}, // "#d7d7ff"
+ {R: 0xD7, G: 0xFF, B: 0x00}, // "#d7ff00"
+ {R: 0xD7, G: 0xFF, B: 0x5F}, // "#d7ff5f"
+ {R: 0xD7, G: 0xFF, B: 0x87}, // "#d7ff87"
+ {R: 0xD7, G: 0xFF, B: 0xAF}, // "#d7ffaf"
+ {R: 0xD7, G: 0xFF, B: 0xD7}, // "#d7ffd7"
+ {R: 0xD7, G: 0xFF, B: 0xFF}, // "#d7ffff"
+ {R: 0xFF, G: 0x00, B: 0x00}, // "#ff0000"
+ {R: 0xFF, G: 0x00, B: 0x5F}, // "#ff005f"
+ {R: 0xFF, G: 0x00, B: 0x87}, // "#ff0087"
+ {R: 0xFF, G: 0x00, B: 0xAF}, // "#ff00af"
+ {R: 0xFF, G: 0x00, B: 0xD7}, // "#ff00d7"
+ {R: 0xFF, G: 0x00, B: 0xFF}, // "#ff00ff"
+ {R: 0xFF, G: 0x5F, B: 0x00}, // "#ff5f00"
+ {R: 0xFF, G: 0x5F, B: 0x5F}, // "#ff5f5f"
+ {R: 0xFF, G: 0x5F, B: 0x87}, // "#ff5f87"
+ {R: 0xFF, G: 0x5F, B: 0xAF}, // "#ff5faf"
+ {R: 0xFF, G: 0x5F, B: 0xD7}, // "#ff5fd7"
+ {R: 0xFF, G: 0x5F, B: 0xFF}, // "#ff5fff"
+ {R: 0xFF, G: 0x87, B: 0x00}, // "#ff8700"
+ {R: 0xFF, G: 0x87, B: 0x5F}, // "#ff875f"
+ {R: 0xFF, G: 0x87, B: 0x87}, // "#ff8787"
+ {R: 0xFF, G: 0x87, B: 0xAF}, // "#ff87af"
+ {R: 0xFF, G: 0x87, B: 0xD7}, // "#ff87d7"
+ {R: 0xFF, G: 0x87, B: 0xFF}, // "#ff87ff"
+ {R: 0xFF, G: 0xAF, B: 0x00}, // "#ffaf00"
+ {R: 0xFF, G: 0xAF, B: 0x5F}, // "#ffaf5f"
+ {R: 0xFF, G: 0xAF, B: 0x87}, // "#ffaf87"
+ {R: 0xFF, G: 0xAF, B: 0xAF}, // "#ffafaf"
+ {R: 0xFF, G: 0xAF, B: 0xD7}, // "#ffafd7"
+ {R: 0xFF, G: 0xAF, B: 0xFF}, // "#ffafff"
+ {R: 0xFF, G: 0xD7, B: 0x00}, // "#ffd700"
+ {R: 0xFF, G: 0xD7, B: 0x5F}, // "#ffd75f"
+ {R: 0xFF, G: 0xD7, B: 0x87}, // "#ffd787"
+ {R: 0xFF, G: 0xD7, B: 0xAF}, // "#ffd7af"
+ {R: 0xFF, G: 0xD7, B: 0xD7}, // "#ffd7d7"
+ {R: 0xFF, G: 0xD7, B: 0xFF}, // "#ffd7ff"
+ {R: 0xFF, G: 0xFF, B: 0x00}, // "#ffff00"
+ {R: 0xFF, G: 0xFF, B: 0x5F}, // "#ffff5f"
+ {R: 0xFF, G: 0xFF, B: 0x87}, // "#ffff87"
+ {R: 0xFF, G: 0xFF, B: 0xAF}, // "#ffffaf"
+ {R: 0xFF, G: 0xFF, B: 0xD7}, // "#ffffd7"
+ {R: 0xFF, G: 0xFF, B: 0xFF}, // "#ffffff"
+ {R: 0x08, G: 0x08, B: 0x08}, // "#080808"
+ {R: 0x12, G: 0x12, B: 0x12}, // "#121212"
+ {R: 0x1C, G: 0x1C, B: 0x1C}, // "#1c1c1c"
+ {R: 0x26, G: 0x26, B: 0x26}, // "#262626"
+ {R: 0x30, G: 0x30, B: 0x30}, // "#303030"
+ {R: 0x3A, G: 0x3A, B: 0x3A}, // "#3a3a3a"
+ {R: 0x44, G: 0x44, B: 0x44}, // "#444444"
+ {R: 0x4E, G: 0x4E, B: 0x4E}, // "#4e4e4e"
+ {R: 0x58, G: 0x58, B: 0x58}, // "#585858"
+ {R: 0x60, G: 0x60, B: 0x60}, // "#606060"
+ {R: 0x66, G: 0x66, B: 0x66}, // "#666666"
+ {R: 0x76, G: 0x76, B: 0x76}, // "#767676"
+ {R: 0x80, G: 0x80, B: 0x80}, // "#808080"
+ {R: 0x8A, G: 0x8A, B: 0x8A}, // "#8a8a8a"
+ {R: 0x94, G: 0x94, B: 0x94}, // "#949494"
+ {R: 0x9E, G: 0x9E, B: 0x9E}, // "#9e9e9e"
+ {R: 0xA8, G: 0xA8, B: 0xA8}, // "#a8a8a8"
+ {R: 0xB2, G: 0xB2, B: 0xB2}, // "#b2b2b2"
+ {R: 0xBC, G: 0xBC, B: 0xBC}, // "#bcbcbc"
+ {R: 0xC6, G: 0xC6, B: 0xC6}, // "#c6c6c6"
+ {R: 0xD0, G: 0xD0, B: 0xD0}, // "#d0d0d0"
+ {R: 0xDA, G: 0xDA, B: 0xDA}, // "#dadada"
+ {R: 0xE4, G: 0xE4, B: 0xE4}, // "#e4e4e4"
+ {R: 0xEE, G: 0xEE, B: 0xEE}, // "#eeeeee"
+ }
+ */
+ XtermRGBPalette [240]RGBColor
+ XtermLabPalette [240]LabColor
+)
+
+func init() {
+ // calculate xterm 240 color RGB palette
+
+ // calculate 6x6x6 color cube
+ cubeLevels := [6]int{0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff}
+ for r := 0; r < 6; r++ {
+ for g := 0; g < 6; g++ {
+ for b := 0; b < 6; b++ {
+ XtermRGBPalette[r*36+g*6+b] = RGBColor{cubeLevels[r], cubeLevels[g], cubeLevels[b]}
+ }
+ }
+ }
+ // calculate grayscale ramp
+ for i := 0; i < 24; i++ {
+ XtermRGBPalette[i+216] = RGBColor{0x08 + i*0xA, 0x08 + i*0xA, 0x08 + i*0xA}
+ }
+
+ // calculate xterm 240 color Lab palette
+ for i, color := range XtermRGBPalette {
+ XtermLabPalette[i] = color.Lab()
+ }
+}