From ecf4c872bb33381abc065f1816e204befcde610c Mon Sep 17 00:00:00 2001 From: historia <[not public]> Date: Wed, 15 Jul 2026 23:44:32 -0400 Subject: initial commit --- color/color.go | 103 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 101 insertions(+), 2 deletions(-) (limited to 'color') diff --git a/color/color.go b/color/color.go index a3cbd16..243869b 100644 --- a/color/color.go +++ b/color/color.go @@ -99,9 +99,52 @@ func (xyz XYZColor) String() string { return fmt.Sprintf("XYZ:{%4.7f %4.7f %4.7f}", xyz.X, xyz.Y, xyz.Z) } +// линейные RGB-компоненты (без гамма-коррекции и ограничения диапазона) +func (xyz XYZColor) rgbLinear() (float64, float64, float64) { + // + // [R] [ 3.2406 -1.5372 -0.4986] [X] + // [G] = [-0.9689 1.8758 0.0415] x [Y] + // [B] [ 0.0557 -0.2040 1.0570] [Z] + // + // Observer. = 2°, Illuminant = D65 + r := xyz.X*3.2406 + xyz.Y*-1.5372 + xyz.Z*-0.4986 + g := xyz.X*-0.9689 + xyz.Y*1.8758 + xyz.Z*0.0415 + b := xyz.X*0.0557 + xyz.Y*-0.2040 + xyz.Z*1.0570 + return r, g, b +} + func (xyz XYZColor) RGB() RGBColor { - // TODO!!! - return RGBColor{0, 0, 0} + + f := func(n float64) int { + if n > 0.0031308 { + n = 1.055*math.Pow(n, 1.0/2.4) - 0.055 + } else { + n = n * 12.92 + } + v := int(math.Round(n * 255.0)) + if v < 0 { + v = 0 + } + if v > 255 { + v = 255 + } + return v + } + + r, g, b := xyz.rgbLinear() + return RGBColor{f(r), f(g), f(b)} +} + +// InGamut сообщает, представим ли цвет в sRGB +func InGamut(c Colorer) bool { + const tolerance = 1e-4 + r, g, b := c.XYZ().rgbLinear() + for _, n := range [3]float64{r, g, b} { + if n < -tolerance || n > 1.0+tolerance { + return false + } + } + return true } func (xyz XYZColor) XYZ() XYZColor { @@ -367,6 +410,35 @@ func Approximate(color Colorer, comparer Comparer) (float64, int) { return bestdist, best + 17 } +// ApproximateAll ищет ближайший цвет среди всех 256 цветов xterm, +// включая 16 системных (их реальный вид зависит от темы терминала) +func ApproximateAll(color Colorer, comparer Comparer) (float64, int) { + bestdist, best := Approximate(color, comparer) + for i, applicant := range SystemLabPalette { + if dist := comparer.Compare(color, applicant); dist < bestdist { + best, bestdist = i+1, dist + } + } + return bestdist, best +} + +// TermRGB возвращает RGB-значение по termbox-атрибуту (1..256). +// Для системных цветов используются стандартные значения xterm. +func TermRGB(c int) RGBColor { + if c-1 < 16 { + return SystemRGBPalette[c-1] + } + return XtermRGBPalette[c-17] +} + +// TermLab возвращает Lab-значение по termbox-атрибуту (1..256) +func TermLab(c int) LabColor { + if c-1 < 16 { + return SystemLabPalette[c-1] + } + return XtermLabPalette[c-17] +} + //color = round(36 * (r * 5) + 6 * (g * 5) + (b * 5) + 16) func HackApproximate(color Colorer) int { c := color.RGB() @@ -623,6 +695,28 @@ var ( */ XtermRGBPalette [240]RGBColor XtermLabPalette [240]LabColor + + // standard xterm defaults for the 16 system colors (0-15); + // actual appearance depends on the terminal theme + SystemRGBPalette = [16]RGBColor{ + {0x00, 0x00, 0x00}, // 0 black + {0xCD, 0x00, 0x00}, // 1 red + {0x00, 0xCD, 0x00}, // 2 green + {0xCD, 0xCD, 0x00}, // 3 yellow + {0x00, 0x00, 0xEE}, // 4 blue + {0xCD, 0x00, 0xCD}, // 5 magenta + {0x00, 0xCD, 0xCD}, // 6 cyan + {0xE5, 0xE5, 0xE5}, // 7 white + {0x7F, 0x7F, 0x7F}, // 8 bright black + {0xFF, 0x00, 0x00}, // 9 bright red + {0x00, 0xFF, 0x00}, // 10 bright green + {0xFF, 0xFF, 0x00}, // 11 bright yellow + {0x5C, 0x5C, 0xFF}, // 12 bright blue + {0xFF, 0x00, 0xFF}, // 13 bright magenta + {0x00, 0xFF, 0xFF}, // 14 bright cyan + {0xFF, 0xFF, 0xFF}, // 15 bright white + } + SystemLabPalette [16]LabColor ) func init() { @@ -646,4 +740,9 @@ func init() { for i, color := range XtermRGBPalette { XtermLabPalette[i] = color.Lab() } + + // calculate Lab palette for the 16 system colors + for i, color := range SystemRGBPalette { + SystemLabPalette[i] = color.Lab() + } } -- cgit v1.2.3