Skip to content

Commit dfe4c47

Browse files
committed
feat: support create Rgb color from Hsl value, issue#40
1 parent b29d2b2 commit dfe4c47

File tree

7 files changed

+226
-57
lines changed

7 files changed

+226
-57
lines changed

README.md

+10
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,16 @@ rgb.Println("rgb color")
417417
rgb.C256().Println("256 color")
418418
```
419419

420+
**More converts functions**:
421+
422+
convert to rgb:
423+
424+
- `func RGBFromSlice(rgb []uint8, isBg ...bool) RGBColor`
425+
- `func RGBFromString(rgb string, isBg ...bool) RGBColor`
426+
- `func HEX(hex string, isBg ...bool) RGBColor`
427+
- `func HSL(h, s, l float32, isBg ...bool) RGBColor`
428+
- `func HSLInt(h, s, l int, isBg ...bool) RGBColor`
429+
420430
## Func refer
421431

422432
There are some useful functions reference

color_rgb.go

+37-7
Original file line numberDiff line numberDiff line change
@@ -71,10 +71,10 @@ const (
7171
// RGBColor{30,144,255, 0}
7272
// RGBColor{30,144,255, 1}
7373
//
74-
// NOTICE: now support RGB color on windows CMD, PowerShell
74+
// NOTICE: now support RGB color on Windows CMD, PowerShell
7575
type RGBColor [4]uint8
7676

77-
// create a empty RGBColor
77+
// create an empty RGBColor
7878
var emptyRGBColor = RGBColor{3: 99}
7979

8080
// RGB color create.
@@ -97,6 +97,16 @@ func Rgb(r, g, b uint8, isBg ...bool) RGBColor { return RGB(r, g, b, isBg...) }
9797
// Bit24 alias of the RGB()
9898
func Bit24(r, g, b uint8, isBg ...bool) RGBColor { return RGB(r, g, b, isBg...) }
9999

100+
// RgbFromInt create instance from int r,g,b value
101+
func RgbFromInt(r, g, b int, isBg ...bool) RGBColor {
102+
return RGB(uint8(r), uint8(g), uint8(b), isBg...)
103+
}
104+
105+
// RgbFromInts create instance from []int r,g,b value
106+
func RgbFromInts(rgb []int, isBg ...bool) RGBColor {
107+
return RGB(uint8(rgb[0]), uint8(rgb[1]), uint8(rgb[2]), isBg...)
108+
}
109+
100110
// HEX create RGB color from a HEX color string.
101111
//
102112
// Usage:
@@ -118,7 +128,8 @@ func HEX(hex string, isBg ...bool) RGBColor {
118128
func Hex(hex string, isBg ...bool) RGBColor { return HEX(hex, isBg...) }
119129

120130
// HSL create RGB color from a hsl value.
121-
func HSL(h, s, l float32, isBg ...bool) RGBColor {
131+
// more see HslToRgb()
132+
func HSL(h, s, l float64, isBg ...bool) RGBColor {
122133
if rgb := HslToRgb(h, s, l); len(rgb) > 0 {
123134
return RGB(rgb[0], rgb[1], rgb[2], isBg...)
124135
}
@@ -128,15 +139,29 @@ func HSL(h, s, l float32, isBg ...bool) RGBColor {
128139
}
129140

130141
// Hsl alias of the HSL()
131-
func Hsl(h, s, l float32, isBg ...bool) RGBColor { return HSL(h, s, l, isBg...) }
142+
func Hsl(h, s, l float64, isBg ...bool) RGBColor { return HSL(h, s, l, isBg...) }
143+
144+
// HSLInt create RGB color from a hsl int value.
145+
// more see HslIntToRgb()
146+
func HSLInt(h, s, l int, isBg ...bool) RGBColor {
147+
if rgb := HslIntToRgb(h, s, l); len(rgb) > 0 {
148+
return RGB(rgb[0], rgb[1], rgb[2], isBg...)
149+
}
150+
151+
// mark is empty
152+
return emptyRGBColor
153+
}
154+
155+
// HslInt alias of the HSLInt()
156+
func HslInt(h, s, l int, isBg ...bool) RGBColor { return HSLInt(h, s, l, isBg...) }
132157

133158
// RGBFromSlice quick RGBColor from slice
134159
func RGBFromSlice(rgb []uint8, isBg ...bool) RGBColor {
135160
return RGB(rgb[0], rgb[1], rgb[2], isBg...)
136161
}
137162

138163
// RGBFromString create RGB color from a string.
139-
// support use color name in the {namedRgbColor}
164+
// support use color name in the {namedRgbMap}
140165
//
141166
// Usage:
142167
// c := RGBFromString("170,187,204")
@@ -145,8 +170,8 @@ func RGBFromSlice(rgb []uint8, isBg ...bool) RGBColor {
145170
// c := RGBFromString("brown")
146171
// c.Print("message with color brown")
147172
func RGBFromString(rgb string, isBg ...bool) RGBColor {
148-
// use color name in the {namedRgbColor}
149-
if rgbVal, ok := namedRgbColor[rgb]; ok {
173+
// use color name in the {namedRgbMap}
174+
if rgbVal, ok := namedRgbMap[rgb]; ok {
150175
rgb = rgbVal
151176
}
152177

@@ -219,6 +244,11 @@ func (c RGBColor) Hex() string {
219244
return fmt.Sprintf("%02x%02x%02x", c[0], c[1], c[2])
220245
}
221246

247+
// RgbString to color code string without prefix. eg: "204,123,56"
248+
func (c RGBColor) RgbString() string {
249+
return fmt.Sprintf("%d,%d,%d", c[0], c[1], c[2])
250+
}
251+
222252
// FullCode to color code string with prefix
223253
func (c RGBColor) FullCode() string {
224254
return c.String()

color_rgb_test.go

+27-13
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,14 @@ func TestPrintRGBColor(t *testing.T) {
206206
HEXStyle("eee", "D50000").Println("deep-purple color")
207207
}
208208

209+
func TestRGBStyle_SetOpts(t *testing.T) {
210+
s := NewRGBStyle(RGB(234, 78, 23), RGB(20, 144, 234))
211+
s.Println("rgb style message")
212+
213+
s.SetOpts(Opts{OpItalic, OpBold, OpUnderscore})
214+
s.Println("RGB style message with options")
215+
}
216+
209217
func testRgbToC256Color(t *testing.T, name string, c RGBColor, expected uint8) {
210218
t.Log("RGB Color:", c.Sprint(name))
211219
t.Log("256 Color:", c.C256().Sprint(name))
@@ -215,21 +223,13 @@ func testRgbToC256Color(t *testing.T, name string, c RGBColor, expected uint8) {
215223
}
216224
}
217225

218-
func TestRGBStyle_SetOpts(t *testing.T) {
219-
s := NewRGBStyle(RGB(234, 78, 23), RGB(20, 144, 234))
220-
s.Println("rgb style message")
221-
222-
s.SetOpts(Opts{OpItalic, OpBold, OpUnderscore})
223-
s.Println("RGB style message with options")
224-
}
225-
226226
func TestRgbToC256(t *testing.T) {
227227
testRgbToC256Color(t, "white", RGB(255, 255, 255), 15)
228228
testRgbToC256Color(t, "red", RGB(255, 0, 0), 9)
229229
testRgbToC256Color(t, "yellow", RGB(255, 255, 0), 11)
230-
testRgbToC256Color(t, "greenBg", RGB(0, 255, 0, true), 10)
230+
testRgbToC256Color(t, "greenBg", RgbFromInt(0, 255, 0, true), 10)
231231
testRgbToC256Color(t, "blueBg", RGB(0, 0, 255, true), 12)
232-
testRgbToC256Color(t, "light blue", RGB(57, 187, 226), 74)
232+
testRgbToC256Color(t, "light blue", RgbFromInts([]int{57, 187, 226}), 74)
233233
}
234234

235235
func TestRgbToC256Background(t *testing.T) {
@@ -259,7 +259,21 @@ func TestRGBColor_C16(t *testing.T) {
259259
}
260260

261261
func TestHSL(t *testing.T) {
262-
hsl := HSL(0.33, 1, 0.5)
262+
// red #ff0000 255, 0, 0
263+
rgb := HSL(0, 1, 0.5)
264+
rgb.Println("rgb color create by HSL; hex:", rgb.Hex(), "rgb:", rgb.RgbString())
265+
266+
rgb = Hsl(0, 1, 0.5)
267+
rgb.Println("rgb color create by HSL; hex:", rgb.Hex(), "rgb:", rgb.RgbString())
268+
269+
rgb = HslInt(0, 100, 50)
270+
rgb.Println("rgb color create by HSL int; hex:", rgb.Hex(), "rgb:", rgb.RgbString())
263271

264-
hsl.Println("rgb color create by HSL")
265-
}
272+
// maroon #800000 128,0,0 0,100%,25%
273+
rgb = HslInt(0, 100, 25)
274+
rgb.Println("rgb color create by HSL int; hex:", rgb.Hex(), "rgb:", rgb.RgbString())
275+
276+
// darkgray #a9a9a9 169,169,169 0,0%,66%
277+
rgb = HslInt(0, 0, 66)
278+
rgb.Println("rgb color create by HSL int; hex:", rgb.Hex(), "rgb:", rgb.RgbString())
279+
}

convert.go

+88-24
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package color
33
import (
44
"fmt"
55
"math"
6+
"sort"
67
"strconv"
78
"strings"
89
)
@@ -628,15 +629,17 @@ func C256ToRgbV1(val uint8) (rgb []uint8) {
628629
* color: hsl(120, 75%, 75%) // pastel green, and so on
629630
*/
630631

631-
// HslToRgbByInt Converts an HSL color value to RGB
632+
// HslIntToRgb Converts an HSL color value to RGB
632633
// Assumes h: 0-360, s: 0-100, l: 0-100
633634
// returns r, g, and b in the set [0, 255].
634635
//
635636
// Usage:
636-
// HslToRgbByInt(120, 100, 50)
637-
// HslToRgbByInt(120, 100, 25)
638-
func HslToRgbByInt(h, s, l int) (rgb []uint8) {
639-
return HslToRgb(float32(h)/360, float32(s)/100, float32(l)/100)
637+
// HslIntToRgb(0, 100, 50) // red
638+
// HslIntToRgb(120, 100, 50) // lime
639+
// HslIntToRgb(120, 100, 25) // dark green
640+
// HslIntToRgb(120, 100, 75) // light green
641+
func HslIntToRgb(h, s, l int) (rgb []uint8) {
642+
return HslToRgb(float64(h)/360, float64(s)/100, float64(l)/100)
640643
}
641644

642645
// HslToRgb Converts an HSL color value to RGB. Conversion formula
@@ -645,50 +648,110 @@ func HslToRgbByInt(h, s, l int) (rgb []uint8) {
645648
// returns r, g, and b in the set [0, 255].
646649
//
647650
// Usage:
648-
// rgbVals := HslToRgb(0.33, 1, 0.5)
649-
func HslToRgb(h, s, l float32) (rgb []uint8) {
650-
var r, g, b float32
651+
// rgbVals := HslToRgb(0, 1, 0.5) // red
652+
func HslToRgb(h, s, l float64) (rgb []uint8) {
653+
var r, g, b float64
651654

652655
if s == 0 { // achromatic
653656
r, g, b = l, l, l
654657
} else {
655-
var hue2rgb = func(p, q, t float32) float32 {
656-
if t < 0 {
658+
var hue2rgb = func(p, q, t float64) float64 {
659+
if t < 0.0 {
657660
t += 1
658661
}
659-
if t > 1 {
662+
if t > 1.0 {
660663
t -= 1
661664
}
662-
if t < 1/6 {
663-
return p + (q-p)*6*t
665+
666+
if t < 1.0/6.0 {
667+
return p + (q-p)*6.0*t
664668
}
665669

666-
if t < 1/2 {
670+
if t < 1.0/2.0 {
667671
return q
668672
}
669-
if t < 2/3 {
670-
return p + (q-p)*(2/3-t)*6
673+
674+
if t < 2.0/3.0 {
675+
return p + (q-p)*(2.0/3.0-t)*6.0
671676
}
672677
return p
673678
}
674679

675680
// q = l < 0.5 ? l * (1 + s) : l + s - l*s
676-
var q float32
681+
var q float64
677682
if l < 0.5 {
678-
q = l * (1 + s)
683+
q = l * (1.0 + s)
679684
} else {
680685
q = l + s - l*s
681686
}
682687

683-
var p = 2*l - q
688+
var p = 2.0*l - q
684689

685-
r = hue2rgb(p, q, h+1/3)
690+
r = hue2rgb(p, q, h+1.0/3.0)
686691
g = hue2rgb(p, q, h)
687-
b = hue2rgb(p, q, h-1/3)
692+
b = hue2rgb(p, q, h-1.0/3.0)
693+
}
694+
695+
// return []uint8{uint8(r * 255), uint8(g * 255), uint8(b * 255)}
696+
return []uint8{
697+
uint8(math.Round(r * 255)),
698+
uint8(math.Round(g * 255)),
699+
uint8(math.Round(b * 255)),
700+
}
701+
}
702+
703+
// RgbToHslInt Converts an RGB color value to HSL. Conversion formula
704+
// Assumes r, g, and b are contained in the set [0, 255] and
705+
// returns [h,s,l] h: 0-360, s: 0-100, l: 0-100.
706+
func RgbToHslInt(r, g, b uint8) []int {
707+
f64s := RgbToHsl(r, g, b)
708+
709+
return []int{int(f64s[0] * 360), int(f64s[1] * 100), int(f64s[2] * 100)}
710+
}
711+
712+
// RgbToHsl Converts an RGB color value to HSL. Conversion formula
713+
// adapted from http://en.wikipedia.org/wiki/HSL_color_space.
714+
// Assumes r, g, and b are contained in the set [0, 255] and
715+
// returns h, s, and l in the set [0, 1].
716+
func RgbToHsl(r, g, b uint8) []float64 {
717+
// to float64
718+
fr, fg, fb := float64(r), float64(g), float64(b)
719+
// percentage
720+
pr, pg, pb := float64(r)/255.0, float64(g)/255.0, float64(b)/255.0
721+
722+
ps := []float64{pr, pg, pb}
723+
sort.Float64s(ps)
724+
725+
min, max := ps[0], ps[2]
726+
// max := math.Max(math.Max(pr, pg), pb)
727+
// min := math.Min(math.Min(pr, pg), pb)
728+
729+
mid := (max + min) / 2
730+
731+
h, s, l := mid, mid, mid
732+
733+
if max == min {
734+
h, s = 0, 0 // achromatic
735+
} else {
736+
var d = max - min
737+
// s = l > 0.5 ? d / (2 - max - min) : d / (max + min)
738+
s = compareF64Val(l > 0.5, d/(2-max-min), d/(max+min))
739+
740+
switch max {
741+
case fr:
742+
// h = (g - b) / d + (g < b ? 6 : 0)
743+
h = (fg - fb) / d
744+
h += compareF64Val(g < b, 6, 0)
745+
case fg:
746+
h = (fb-fr)/d + 2
747+
case fb:
748+
h = (fr-fg)/d + 4
749+
}
750+
751+
h /= 6
688752
}
689753

690-
// return [math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
691-
return []uint8{uint8(r * 255 / 100), uint8(g * 255 / 100), uint8(b * 255 / 100)}
754+
return []float64{h, s, l}
692755
}
693756

694757
/**************************************************************
@@ -707,12 +770,13 @@ func HslToRgb(h, s, l float32) (rgb []uint8) {
707770
// Assumes h: 0-360, s: 0-100, l: 0-100
708771
// returns r, g, and b in the set [0, 255].
709772
func HsvToRgb(h, s, v int) (rgb []uint8) {
773+
// TODO ...
710774
return
711775
}
712776

713777
// Named rgb colors
714778
// https://www.w3.org/TR/css-color-3/#svg-color
715-
var namedRgbColor = map[string]string{
779+
var namedRgbMap = map[string]string{
716780
"aliceblue": "240,248,255", // #F0F8FF
717781
"antiquewhite": "250,235,215", // #FAEBD7
718782
"aqua": "0,255,255", // #00FFFF

convert_test.go

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package color
2+
3+
import (
4+
"fmt"
5+
"testing"
6+
7+
"github.com/stretchr/testify/assert"
8+
)
9+
10+
func TestRgb2basic(t *testing.T) {
11+
assert.Equal(t, uint8(31), Rgb2basic(134, 56, 56, false))
12+
assert.Equal(t, uint8(41), Rgb2basic(134, 56, 56, true))
13+
assert.Equal(t, uint8(46), Rgb2basic(57, 187, 226, true))
14+
}
15+
16+
func TestHslToRgb(t *testing.T) {
17+
// red #ff0000 255,0,0 0,100%,50%
18+
rgbVal := HslToRgb(0, 1, 0.5)
19+
// fmt.Println(rgbVal)
20+
assert.Equal(t, []uint8{255, 0, 0}, rgbVal)
21+
22+
rgbVal = HslIntToRgb(0, 100, 50)
23+
// fmt.Println(rgbVal)
24+
assert.Equal(t, []uint8{255, 0, 0}, rgbVal)
25+
26+
rgbVal = HslIntToRgb(0, 100, 25)
27+
// fmt.Println(rgbVal)
28+
assert.Equal(t, []uint8{128, 0, 0}, rgbVal)
29+
30+
// darkgray #a9a9a9 169,169,169 0,0%,66%
31+
rgbVal = HslIntToRgb(0, 0, 66)
32+
fmt.Println(rgbVal)
33+
assert.Equal(t, []uint8{168, 168, 168}, rgbVal)
34+
35+
rgbVal = HslToRgb(0, 0, 0.6627)
36+
fmt.Println(rgbVal)
37+
assert.Equal(t, []uint8{169, 169, 169}, rgbVal)
38+
39+
hslVal := RgbToHslInt(rgbVal[0], rgbVal[1], rgbVal[2])
40+
fmt.Println(hslVal)
41+
assert.Equal(t, []int{0, 0, 66}, hslVal)
42+
43+
hslFVal := RgbToHsl(rgbVal[0], rgbVal[1], rgbVal[2])
44+
fmt.Println(hslFVal)
45+
}

0 commit comments

Comments
 (0)