aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPavlov Eugene <blooderast@gmail.com>2015-10-08 15:07:12 +0300
committerPavlov Eugene <blooderast@gmail.com>2015-10-08 15:07:12 +0300
commitf04712a908761f4277a94f36117694edadbafaa8 (patch)
tree0613d724d79dbf27dcbe79175a25b4a2a1dc9ca5
parent7bddc77bde6c2821f187246a687fb4adfcc6bbec (diff)
downloadxterm256-color-palette-f04712a908761f4277a94f36117694edadbafaa8.tar.gz
Prealfa
-rw-r--r--chart.go409
-rw-r--r--color/color.go649
2 files changed, 1058 insertions, 0 deletions
diff --git a/chart.go b/chart.go
new file mode 100644
index 0000000..dd4ac10
--- /dev/null
+++ b/chart.go
@@ -0,0 +1,409 @@
+package main
+
+import (
+ "fmt"
+ "math"
+
+ "github.com/kutuluk/xterm-color-chart/color"
+ "github.com/nsf/termbox-go"
+)
+
+// обертка над termbox'ом
+type Screen struct {
+ Buffer []termbox.Cell
+ Width int
+ Height int
+}
+
+func (s *Screen) reScreen() {
+ s.Buffer = termbox.CellBuffer()
+ s.Width, s.Height = termbox.Size()
+}
+
+func (s *Screen) Init() error {
+ err := termbox.Init()
+ if err == nil {
+ s.reScreen()
+ }
+ return err
+}
+
+func (s *Screen) Flush() {
+ termbox.Flush()
+ s.reScreen()
+}
+
+func (s *Screen) Clear(fg, bg termbox.Attribute) {
+ termbox.Clear(fg, bg)
+ s.reScreen()
+}
+
+var screen Screen
+
+func termNative(c int) termbox.Attribute {
+ return termbox.Attribute(c + 1)
+}
+
+func printString(x, y int, s string) {
+ // TODO срабатывает перенос на следующую строку при достижении правого края
+ // и ошибка при выходе из границ буфера снизу
+ offsetX := 0
+ offsetY := 0
+ for _, char := range s {
+ if char == '\n' {
+ offsetX = 0
+ offsetY++
+ } else {
+ screen.Buffer[x+offsetX+(y+offsetY)*screen.Width].Ch = char
+ offsetX++
+ }
+ }
+}
+
+func printLine(x, y int, s string) {
+ printLineAttr(x, y, s, termbox.ColorWhite, background)
+}
+
+func printLineAttr(x, y int, s string, fg, bg termbox.Attribute) {
+ offsetX := 0
+ offsetY := 0
+ for _, char := range s {
+ if char == '\n' {
+ offsetX = 0
+ offsetY++
+ } else {
+ termbox.SetCell(x+offsetX, y+offsetY, char, fg, bg)
+ offsetX++
+ }
+ }
+}
+
+func DrawColor(x, y int, c int) {
+
+ Grayscale := []byte{
+ 16, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243,
+ 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 231,
+ }
+
+ for xx := 0; xx < 13; xx++ {
+ termbox.SetCell(x+xx, y, ' ', termbox.ColorBlack, termbox.Attribute(c))
+ }
+
+ // round(36 * (r * 5) + 6 * (g * 5) + (b * 5) + 16)
+ // https://gist.github.com/MicahElliott/719710
+
+ v := int(0.2125*float64(color.XtermRGBPalette[c].R) + 0.7154*float64(color.XtermRGBPalette[c].G) + 0.0721*float64(color.XtermRGBPalette[c].B))
+
+ fg := termbox.ColorRed
+ // if v < 128 {
+ // fg = termbox.ColorWhite
+ //fg = termbox.Attribute(231)
+ delta := 64
+ center := 128
+ if (v > (center - delta)) && (v <= center) {
+ fg = termbox.Attribute(Grayscale[25])
+ } else if (v > center) && (v < (center + delta)) {
+ fg = termbox.Attribute(Grayscale[0])
+ } else {
+ //fg = termbox.Attribute(Grayscale[12])
+ fg = termbox.Attribute(Grayscale[25-int(v/11)])
+ }
+
+ printLineAttr(x+1, y, fmt.Sprintf("%03d #%s", int(c), color.XtermRGBPalette[c]), fg, termbox.Attribute(c))
+}
+
+func DrawBox(left, top, d, l int) {
+ var approxer string
+ switch currentComparer {
+ case 0:
+ approxer = "CIE76"
+ case 1:
+ approxer = "CIE94"
+ case 2:
+ approxer = "CIE2000"
+ }
+
+ r := d / 2
+ for y := 0; y < d; y++ {
+ for x := 0; x < d; x++ {
+ a := 100.0 * float64(x-r) / float64(r)
+ b := -100.0 * float64(y-r) / float64(r)
+ _, c := color.Approximate(color.LabColor{float64(l), a, b}, comparers[currentComparer])
+ //termbox.SetCell(x*2+left, y+top, ' ', background, termbox.Attribute(color))
+ //termbox.SetCell(x*2+left+1, y+top, ' ', background, termbox.Attribute(color))
+ fg := int(background)
+ if y == 1 || y == 2 || y == d-2 {
+ fgLab := color.XtermRGBPalette[c-17].Lab()
+ if l > 50 {
+ fgLab.L -= 50
+ //fgLab.L = 20
+ } else {
+ fgLab.L += 50
+ //fgLab.L = 70
+ }
+ _, fg = color.Approximate(fgLab, color.DeltaCIE2000{})
+ }
+ termbox.SetCell(x*2+left, y+top, ' ', termbox.Attribute(fg), termbox.Attribute(c))
+ termbox.SetCell(x*2+left+1, y+top, ' ', termbox.Attribute(fg), termbox.Attribute(c))
+ }
+
+ }
+
+ labLabel := "CIE L*a*b color space"
+ if len(labLabel)+4 < d*2 {
+ printString(left+2, top+1, labLabel)
+ printString(left+2, top+2, fmt.Sprintf("Lightness = %d%%", l))
+ }
+ //printString(left+2, top+4, fmt.Sprintln("Press Up/Down key for change"))
+
+ approxerLabel := fmt.Sprintf("Approximate by %s algorithm", approxer)
+ if len(approxerLabel)+4 <= d*2 {
+ printString(left+2, top+d-2, approxerLabel)
+ }
+ //printString(left+2, top+7, fmt.Sprintln("Press Left/Right key for change"))
+}
+
+func DrawCircle(left, top, r int, l float64) {
+
+ var ch [2]rune
+ point := func(xb, yb int) {
+ xl := xb / r
+ yl := yb / r
+ distance, color := color.Approximate(color.LChColor{l, math.Sqrt(float64(xl*xl) + float64(yl*yl)), 180.0 * math.Atan2(float64(xl), float64(yl))}, comparers[currentComparer])
+ //distance, color := color.Palette(color.LabColor{l, float64(xb * 120.0 / r), float64(-yb * 120.0 / r)}, color.DeltaCIE94{})
+ if distance < 50 {
+ ch[0] = ' '
+ ch[1] = ' '
+ } else {
+ //ch[0] = '¤'
+ //ch[1] = '¤'
+ ch[0] = '('
+ ch[1] = ')'
+ }
+ termbox.SetCell((xb+r)*2+left, yb+r+top, ch[0], termbox.Attribute(0), termbox.Attribute(color))
+ termbox.SetCell((xb+r)*2+left+1, yb+r+top, ch[1], termbox.Attribute(0), termbox.Attribute(color))
+ }
+
+ // алгоритм Брезенхэма
+ line := func(x, y1, y2 int) {
+ for yf := y1; yf <= y2; yf++ {
+ point(x, yf)
+ }
+ }
+
+ x := 0
+ y := r
+ d := 3 - 2*r
+ for x <= y {
+ line(x, -y, y)
+ line(-x, -y, y)
+ line(y, -x, x)
+ line(-y, -x, x)
+ if d < 0 {
+ d += 4*x + 6
+ } else {
+ d += 4*(x-y) + 10
+ y--
+ }
+ x++
+ }
+}
+
+func DrawColorCell(x, y, c int) {
+ termbox.SetCell(x, y, ' ', termbox.Attribute(1), termbox.Attribute(c))
+ termbox.SetCell(x+1, y, ' ', termbox.Attribute(1), termbox.Attribute(c))
+}
+
+// left, top - левый верхний угол отрисовки палитры
+// row - количество кубов 6x6 по горизонтали
+// num - признак отрисовки номеров цветов
+func DrawPalette(left, top, row int, num bool) {
+ var numOffset int
+ if num {
+ numOffset = 4
+ }
+
+ //bh := [2]termbox.Attribute{237, background}
+ bh := [2]termbox.Attribute{236, background}
+ //th := [2]termbox.Attribute{232, 241}
+ th := [2]termbox.Attribute{243, 243}
+
+ cc := 1
+
+ printLineAttr(left, top, fmt.Sprintln("16 system colors"), text, background)
+
+ printLineAttr(left, top+2, fmt.Sprintf("%3d ", cc-1), th[1], bh[1])
+ printLineAttr(left+numOffset+16*2, top+2, fmt.Sprintf(" %-3d", cc+14), th[1], bh[1])
+
+ for x := 0; x < 16; x++ {
+ DrawColorCell(left+numOffset+x*2, top+2, cc)
+ cc++
+ }
+
+ padding := 1
+ if num {
+ // padding = 0
+ }
+
+ printLineAttr(left, top+4, fmt.Sprintln("216 colors of color cube 6x6x6"), text, background)
+ col := int(6 / row) // ширина
+ for l := 0; l < row; l++ { // l - вертикальный счетчик
+ for k := 0; k < col; k++ { // k - горизонтальный счетчик
+ for y := 0; y < 6; y++ {
+ h := y % 2
+ if h != 0 {
+ h = 1
+ }
+ printLineAttr(left+k*numOffset*2+k*(6+padding)*2, top+l*(6+padding)+y+6, fmt.Sprintf("%3d ", cc-1), th[h], bh[h])
+ printLineAttr(left+k*numOffset*2+k*(6+padding)*2+16, top+l*(6+padding)+y+6, fmt.Sprintf(" %-3d", cc+4), th[h], bh[h])
+ for x := 0; x < 6; x++ {
+ DrawColorCell(left+numOffset+k*(6+padding)*2+k*numOffset*2+x*2, top+l*(6+padding)+y+6, cc)
+ cc++
+ }
+ }
+ }
+ }
+
+ printLineAttr(left, top+row*7+6, fmt.Sprintln("24 grayscale colors"), text, background)
+ for y := 0; y < 2; y++ {
+ for x := 0; x < 12; x++ {
+ DrawColorCell(left+numOffset+x*2, top+row*7+y+8, cc)
+ cc++
+ }
+ }
+
+ printLineAttr(left, top+row*7+8, fmt.Sprintf("%3d ", 232), th[1], bh[1])
+ printLineAttr(left+28, top+row*7+8, fmt.Sprintf(" %-3d", 243), th[1], bh[1])
+ printLineAttr(left, top+row*7+9, fmt.Sprintf("%3d ", 244), th[1], bh[1])
+ printLineAttr(left+28, top+row*7+9, fmt.Sprintf(" %-3d ", 255), th[1], bh[1])
+}
+
+func Fill(x1, y1, x2, y2 int, fg, bg termbox.Attribute) {
+ for y := y1; y <= y2; y++ {
+ for x := x1; x <= x2; x++ {
+ termbox.SetCell(x, y, ' ', fg, bg)
+ }
+ }
+}
+
+var (
+ text, background termbox.Attribute
+ lightness int
+ helpVisible bool
+)
+
+func DrawChart() {
+ screen.Clear(text, background)
+ //Fill(0, 0, screen.Width-1, screen.Height-1, text, background)
+
+ Fill(0, 0, screen.Width-1, 0, background, background+8)
+ printString(2, 0, "XTerm 256 color palette chart")
+ printString(screen.Width-19, 0, "F1 Help F10 Exit")
+ printLineAttr(screen.Width-19, 0, "F1", background+20, background+8)
+ printLineAttr(screen.Width-10, 0, "F10", background+20, background+8)
+
+ paletteSize := 42
+
+ boxSize := screen.Height - 3
+ w := int((screen.Width - paletteSize - 6) / 2)
+ if w < boxSize {
+ boxSize = w
+ }
+
+ DrawBox(2, 2, boxSize, lightness)
+ // http://snag.gy/XMro8.jpg - картинка для сравнения
+
+ helpText := []string{
+ "Control keys:",
+ "",
+ "F1 - show/hide this help",
+ "Up/Down - change lightness",
+ "Left/Right - change approximation method",
+ "",
+ "CIE76 - fastest",
+ "CIE94 - middle",
+ "CIE2000 - slowest",
+ "",
+ "",
+ }
+
+ xc := boxSize*2 + 4
+ if xc < 2 {
+ xc = 2
+ }
+ yc := 2
+
+ if helpVisible {
+ for i, line := range helpText {
+ printString(xc, yc+i, line)
+ }
+ } else {
+ DrawPalette(xc, yc, 3, true)
+ }
+
+ screen.Flush()
+}
+
+var comparers []color.Comparer
+var currentComparer int
+
+func main() {
+
+ err := screen.Init()
+ if err != nil {
+ panic(err)
+ }
+ defer termbox.Close()
+ termbox.SetOutputMode(termbox.Output256)
+ //termbox.SetInputMode(termbox.InputEsc)
+
+ background = termNative(234)
+ text = background + 12
+ lightness = 100
+
+ comparers = append(comparers, color.DeltaCIE76{}, color.DeltaCIE94{}, color.DeltaCIE2000{})
+
+ //size := int(h/2 - 3)
+ //DrawCircle(2, 1, size, 1.0)
+
+ DrawChart()
+
+ for loop := true; loop; {
+ switch ev := termbox.PollEvent(); ev.Type {
+ case termbox.EventKey:
+ switch ev.Key {
+ case termbox.KeyCtrlQ, termbox.KeyF10:
+ loop = false
+ case termbox.KeyCtrlH, termbox.KeyF1:
+ helpVisible = !helpVisible
+ DrawChart()
+ case termbox.KeyArrowUp:
+ if lightness < 100 {
+ lightness = lightness + 10.0
+ DrawChart()
+ }
+ case termbox.KeyArrowDown:
+ if lightness > 0 {
+ lightness = lightness - 10.0
+ DrawChart()
+ }
+ case termbox.KeyArrowLeft:
+ if currentComparer > 0 {
+ currentComparer--
+ } else {
+ currentComparer = len(comparers) - 1
+ }
+ DrawChart()
+ case termbox.KeyArrowRight:
+ if currentComparer < 2 {
+ currentComparer++
+ } else {
+ currentComparer = 0
+ }
+ DrawChart()
+ }
+ case termbox.EventResize:
+ DrawChart()
+ }
+ }
+}
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()
+ }
+}