v0.1.3 ANSI 16 indices and RGB to 16
This commit is contained in:
6 files changed
+75
-8
No files matched your search
@@ -77,15 +77,22 @@ var Accent = color.MustParseHex("#ff8800")
|
||||
accent = { r = 255, g = 136, b = 0 }
|
||||
```
|
||||
|
||||
## With terminal
|
||||
## Terminal palettes
|
||||
|
||||
`terminal.Cell` carries `color.RGB` directly. Quantization stays device-side:
|
||||
Quantization for terminals that cannot take 24-bit color:
|
||||
|
||||
```go
|
||||
idx := terminal.RGBTo256(color.EmeraldGreen) // xterm-256 index
|
||||
idx := color.RGBTo256(color.EmeraldGreen) // xterm-256 index, Redmean LUT
|
||||
ansi := color.RGBTo16(color.EmeraldGreen) // ANSI 16 index, for text consoles
|
||||
```
|
||||
|
||||
Note: when `Cell.Attrs` sets `AttrFg256` / `AttrBg256`, `Cell.Fg.R` / `Cell.Bg.R` hold a palette index, not a channel. Such values are not colors and must not be passed to this package.
|
||||
`ANSIBlack` .. `ANSIBrightWhite` name the 16 indices (SGR `30-37`/`90-97`).
|
||||
The terminal's theme picks their shade, so they suit semantic colors such as
|
||||
log levels. `RGBTo16` measures against xterm's default shades.
|
||||
|
||||
`terminal.Cell` carries `color.RGB` directly. When `Cell.Attrs` sets
|
||||
`AttrFg256` / `AttrBg256`, `Cell.Fg.R` / `Cell.Bg.R` hold a palette index, not
|
||||
a channel. Such values are not colors and must not be passed to this package.
|
||||
|
||||
## Concurrency
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ func showGrayscaleRamp() {
|
||||
idx := color.Gray256(step)
|
||||
fmt.Print(block256(idx, 2))
|
||||
}
|
||||
fmt.Println("\n")
|
||||
fmt.Print("\n\n")
|
||||
}
|
||||
|
||||
// =====================================================================
|
||||
|
||||
@@ -6,8 +6,9 @@
|
||||
// image/color.Color, so values pass directly into image, draw, and GUI
|
||||
// toolkit pipelines without conversion.
|
||||
//
|
||||
// Device concerns — terminal capability detection, xterm-256 quantization,
|
||||
// SGR emission, framebuffer formats — belong in the consuming package.
|
||||
// Palette quantization (RGBTo256, RGBTo16) lives here; device concerns —
|
||||
// terminal capability detection, SGR emission, framebuffer formats — belong
|
||||
// in the consuming package.
|
||||
//
|
||||
// # Operations
|
||||
//
|
||||
|
||||
@@ -130,6 +130,47 @@ func computeRedmean256(c RGB) uint8 {
|
||||
|
||||
var cubeValues = [6]uint8{0, 95, 135, 175, 215, 255}
|
||||
|
||||
// ANSI 16 color indices, sent as SGR 30-37/90-97 (40-47/100-107 background).
|
||||
// The terminal's theme picks the shade; a text console renders them too.
|
||||
const (
|
||||
ANSIBlack uint8 = iota
|
||||
ANSIRed
|
||||
ANSIGreen
|
||||
ANSIYellow
|
||||
ANSIBlue
|
||||
ANSIMagenta
|
||||
ANSICyan
|
||||
ANSIWhite
|
||||
ANSIBrightBlack
|
||||
ANSIBrightRed
|
||||
ANSIBrightGreen
|
||||
ANSIBrightYellow
|
||||
ANSIBrightBlue
|
||||
ANSIBrightMagenta
|
||||
ANSIBrightCyan
|
||||
ANSIBrightWhite
|
||||
)
|
||||
|
||||
// xterm's default shades for the ANSI 16 indices, the reference for RGBTo16
|
||||
var ansi16 = [16]RGB{
|
||||
{0, 0, 0}, {205, 0, 0}, {0, 205, 0}, {205, 205, 0},
|
||||
{0, 0, 238}, {205, 0, 205}, {0, 205, 205}, {229, 229, 229},
|
||||
{127, 127, 127}, {255, 0, 0}, {0, 255, 0}, {255, 255, 0},
|
||||
{92, 92, 255}, {255, 0, 255}, {0, 255, 255}, {255, 255, 255},
|
||||
}
|
||||
|
||||
// RGBTo16 converts 24-bit RGB to the nearest ANSI 16 index (Redmean distance
|
||||
// to xterm's default shades), for terminals limited to 16 colors.
|
||||
func RGBTo16(c RGB) uint8 {
|
||||
best, minDist := ANSIBlack, 1<<30
|
||||
for i, cand := range ansi16 {
|
||||
if d := RedmeanDistance(c, cand); d < minDist {
|
||||
best, minDist = uint8(i), d
|
||||
}
|
||||
}
|
||||
return best
|
||||
}
|
||||
|
||||
// RGBTo256 converts 24-bit RGB to the nearest xterm-256 palette index.
|
||||
// O(1) via the Redmean LUT; the first call builds it.
|
||||
func RGBTo256(c RGB) uint8 {
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package color
|
||||
|
||||
import "testing"
|
||||
|
||||
// Each reference shade maps to its own index, so the constants, the table and
|
||||
// the search agree; the named primaries land on the bright indices like xterm.
|
||||
func TestRGBTo16MapsReferenceShadesToTheirIndex(t *testing.T) {
|
||||
for i, c := range ansi16 {
|
||||
if got := RGBTo16(c); got != uint8(i) {
|
||||
t.Errorf("shade %d %s: index %d", i, c.Hex(), got)
|
||||
}
|
||||
}
|
||||
for c, want := range map[RGB]uint8{Red: ANSIBrightRed, Black: ANSIBlack, White: ANSIBrightWhite, {170, 0, 0}: ANSIRed} {
|
||||
if got := RGBTo16(c); got != want {
|
||||
t.Errorf("%s: index %d, want %d", c.Hex(), got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user