Skip to content

Commit

Permalink
feat: support convert rgb to 16-color code. close: #31
Browse files Browse the repository at this point in the history
  • Loading branch information
inhere committed Dec 2, 2020
1 parent 07702a1 commit 079365c
Show file tree
Hide file tree
Showing 8 changed files with 116 additions and 7 deletions.
4 changes: 2 additions & 2 deletions README.md
Expand Up @@ -24,9 +24,9 @@ Now, 256 colors and RGB colors have also been supported to work in Windows CMD a
## Features

- Simple to use, zero dependencies
- Supports rich color output: 16-color, 256-color, true color (24-bit, RGB)
- Supports rich color output: 16-color (4-bit), 256-color (8-bit), true color (24-bit, RGB)
- 16-color output is the most commonly used and most widely supported, working on any Windows version
- Since `v1.2.4` **the 256-color, true color (24-bit) support windows CMD and PowerShell**
- Since `v1.2.4` **the 256-color (8-bit), true color (24-bit) support windows CMD and PowerShell**
- See [this gist](https://gist.github.com/XVilka/8346728) for information on true color support
- Generic API methods: `Print`, `Printf`, `Println`, `Sprint`, `Sprintf`
- Supports HTML tag-style color rendering, such as `<green>message</>`. Support working on windows `cmd` `powerShell`
Expand Down
2 changes: 2 additions & 0 deletions _examples/ref/cmd_color.go
@@ -1,3 +1,5 @@
//+build windows

package main

import (
Expand Down
6 changes: 6 additions & 0 deletions color_16.go
Expand Up @@ -40,6 +40,12 @@ func (o Opts) String() string {
* Basic 16 color definition
*************************************************************/

// Base value for foreground/background color
const (
FgBase uint8 = 30
BgBase uint8 = 40
)

// Foreground colors. basic foreground colors 30 - 37
const (
FgBlack Color = iota + 30
Expand Down
7 changes: 4 additions & 3 deletions color_rgb.go
Expand Up @@ -173,9 +173,10 @@ func (c RGBColor) C256() Color256 {
}

// C16 returns the closest approximate 256 (8 bit) color
// func (c RGBColor) C16() Color {
// return Color()
// }
// refer https://github.com/radareorg/radare2/blob/master/libr/cons/rgb.c#L249-L271
func (c RGBColor) C16() Color {
return Color(RgbToAnsi(c[0], c[1], c[2], c[3] == AsBg))
}

/*************************************************************
* RGB Style
Expand Down
10 changes: 10 additions & 0 deletions color_rgb_test.go
Expand Up @@ -2,6 +2,8 @@ package color

import (
"testing"

"github.com/stretchr/testify/assert"
)

func testRgbToC256Color(t *testing.T, name string, c RGBColor, expected uint8) {
Expand Down Expand Up @@ -45,3 +47,11 @@ func TestRgbToC256Background(t *testing.T) {
t.Errorf("background color didn't have background prefix: %v", prefix)
}
}

func TestRGBColor_C16(t *testing.T) {
rgb := RGB(57, 187, 226)
assert.Equal(t, "36", rgb.C16().String())

rgb = RGB(57, 187, 226, true)
assert.Equal(t, "46", rgb.C16().String())
}
4 changes: 3 additions & 1 deletion go.mod
Expand Up @@ -2,4 +2,6 @@ module github.com/gookit/color

go 1.12

require github.com/stretchr/testify v1.3.0
require (
github.com/stretchr/testify v1.3.0
)
55 changes: 54 additions & 1 deletion utils.go
Expand Up @@ -171,7 +171,7 @@ func HexToRgb(hex string) (rgb []int) {
// Rgb2hex alias of the RgbToHex()
func Rgb2hex(rgb []int) string { return RgbToHex(rgb) }

// RgbToHex convert RGB to hex code
// RgbToHex convert RGB-code to hex-code
// Usage:
// hex := RgbToHex([]int{170, 187, 204}) // hex: "aabbcc"
func RgbToHex(rgb []int) string {
Expand All @@ -183,6 +183,51 @@ func RgbToHex(rgb []int) string {
return strings.Join(hexNodes, "")
}

// Rgb2ansi alias of the RgbToAnsi()
func Rgb2ansi(r, g, b uint8, isBg bool) uint8 {
return RgbToAnsi(r, g, b, isBg)
}

// RgbToAnsi convert RGB-code to 16-code
func RgbToAnsi(r, g, b uint8, isBg bool) uint8 {
var bright, c, k uint8

base := compareVal(isBg, BgBase, FgBase)

// eco bright-specific
if r == 0x80 && g == 0x80 && b == 0x80 { // 0x80=128
bright = 53
} else if r == 0xff || g == 0xff || b == 0xff { // 0xff=255
bright = 60
} // else bright = 0

if r == g && g == b {
// 0x7f=127
// r = (r > 0x7f) ? 1 : 0;
r = compareVal(r > 0x7f, 1, 0)
g = compareVal(g > 0x7f, 1, 0)
b = compareVal(b > 0x7f, 1, 0)
} else {
k = (r + g + b) / 3;

// r = (r >= k) ? 1 : 0;
r = compareVal(r >= k, 1, 0)
g = compareVal(g >= k, 1, 0)
b = compareVal(b >= k, 1, 0)
}

// c = (r ? 1 : 0) + (g ? (b ? 6 : 2) : (b ? 4 : 0))
c = compareVal(r > 0, 1, 0)

if g > 0 {
c += compareVal(b > 0, 6, 2)
} else {
c += compareVal(b > 0, 4, 0)
}

return base + bright + c
}

/*************************************************************
* print methods(will auto parse color tags)
*************************************************************/
Expand Down Expand Up @@ -308,6 +353,14 @@ func Text(s string) string {
// return runtime.GOOS == "windows"
// }

// equals: return ok ? val1 : val2
func compareVal(ok bool, val1, val2 uint8) uint8 {
if ok {
return val1
}
return val2
}

func saveInternalError(err error) {
if err != nil {
errors = append(errors, err)
Expand Down
35 changes: 35 additions & 0 deletions utils_test.go
@@ -1,6 +1,7 @@
package color

import (
"fmt"
"testing"

"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -43,3 +44,37 @@ func TestRgbToHex(t *testing.T) {
assert.Equal(t, Rgb2hex(item.given), item.want)
}
}

func TestRgbToAnsi(t *testing.T) {
tests := []struct {
want uint8
rgb []uint8
isBg bool
}{
{40, []uint8{102, 102, 102}, true},
{37, []uint8{204, 204, 204}, false},
{47, []uint8{170, 78, 204}, true},
{37, []uint8{170, 153, 245}, false},
{30, []uint8{127, 127, 127}, false},
{40, []uint8{127, 127, 127}, true},
{90, []uint8{128, 128, 128}, false},
{97, []uint8{34, 56, 255}, false},
{31, []uint8{134, 56, 56}, false},
{30, []uint8{0, 0, 0}, false},
{40, []uint8{0, 0, 0}, true},
{97, []uint8{255, 255, 255}, false},
{107, []uint8{255, 255, 255}, true},
}

for _, item := range tests {
r, g, b := item.rgb[0], item.rgb[1], item.rgb[2]

assert.Equal(
t,
item.want,
RgbToAnsi(r, g, b, item.isBg),
fmt.Sprint("rgb=", item.rgb, ", is bg? ", item.isBg),
)
assert.Equal(t, item.want, Rgb2ansi(r, g, b, item.isBg))
}
}

0 comments on commit 079365c

Please sign in to comment.