From 07233ddb0c91803d19bb1511238ba6a02049e89b Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Fri, 30 Sep 2022 03:56:27 +0200 Subject: [PATCH 1/2] test: add RGBA tests --- color_test.go | 144 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 142 insertions(+), 2 deletions(-) diff --git a/color_test.go b/color_test.go index bbe07f14..cc0ab55c 100644 --- a/color_test.go +++ b/color_test.go @@ -7,8 +7,6 @@ import ( ) func TestSetColorProfile(t *testing.T) { - t.Parallel() - tt := []struct { profile termenv.Profile input string @@ -85,3 +83,145 @@ func TestHexToColor(t *testing.T) { } } } + +func TestRGBA(t *testing.T) { + tt := []struct { + profile termenv.Profile + darkBg bool + input TerminalColor + expected uint + }{ + // lipgloss.Color + { + termenv.TrueColor, + true, + Color("#FF0000"), + 0xFF0000, + }, + { + termenv.TrueColor, + true, + Color("9"), + 0xFF0000, + }, + { + termenv.TrueColor, + true, + Color("21"), + 0x0000FF, + }, + // lipgloss.AdaptiveColor + { + termenv.TrueColor, + true, + AdaptiveColor{Dark: "#FF0000", Light: "#0000FF"}, + 0xFF0000, + }, + { + termenv.TrueColor, + false, + AdaptiveColor{Dark: "#FF0000", Light: "#0000FF"}, + 0x0000FF, + }, + { + termenv.TrueColor, + true, + AdaptiveColor{Dark: "9", Light: "21"}, + 0xFF0000, + }, + { + termenv.TrueColor, + false, + AdaptiveColor{Dark: "9", Light: "21"}, + 0x0000FF, + }, + // lipgloss.CompleteColor + { + termenv.TrueColor, + true, + CompleteColor{TrueColor: "#FF0000", ANSI256: "231", ANSI: "12"}, + 0xFF0000, + }, + { + termenv.ANSI256, + true, + CompleteColor{TrueColor: "#FF0000", ANSI256: "231", ANSI: "12"}, + 0xFFFFFF, + }, + { + termenv.ANSI, + true, + CompleteColor{TrueColor: "#FF0000", ANSI256: "231", ANSI: "12"}, + 0x0000FF, + }, + // lipgloss.CompleteAdaptiveColor + // dark + { + termenv.TrueColor, + true, + CompleteAdaptiveColor{ + Dark: CompleteColor{TrueColor: "#FF0000", ANSI256: "231", ANSI: "12"}, + Light: CompleteColor{TrueColor: "#0000FF", ANSI256: "231", ANSI: "12"}, + }, + 0xFF0000, + }, + { + termenv.ANSI256, + true, + CompleteAdaptiveColor{ + Dark: CompleteColor{TrueColor: "#FF0000", ANSI256: "231", ANSI: "12"}, + Light: CompleteColor{TrueColor: "#FF0000", ANSI256: "21", ANSI: "12"}, + }, + 0xFFFFFF, + }, + { + termenv.ANSI, + true, + CompleteAdaptiveColor{ + Dark: CompleteColor{TrueColor: "#FF0000", ANSI256: "231", ANSI: "12"}, + Light: CompleteColor{TrueColor: "#FF0000", ANSI256: "231", ANSI: "9"}, + }, + 0x0000FF, + }, + // light + { + termenv.TrueColor, + false, + CompleteAdaptiveColor{ + Dark: CompleteColor{TrueColor: "#FF0000", ANSI256: "231", ANSI: "12"}, + Light: CompleteColor{TrueColor: "#0000FF", ANSI256: "231", ANSI: "12"}, + }, + 0x0000FF, + }, + { + termenv.ANSI256, + false, + CompleteAdaptiveColor{ + Dark: CompleteColor{TrueColor: "#FF0000", ANSI256: "231", ANSI: "12"}, + Light: CompleteColor{TrueColor: "#FF0000", ANSI256: "21", ANSI: "12"}, + }, + 0x0000FF, + }, + { + termenv.ANSI, + false, + CompleteAdaptiveColor{ + Dark: CompleteColor{TrueColor: "#FF0000", ANSI256: "231", ANSI: "12"}, + Light: CompleteColor{TrueColor: "#FF0000", ANSI256: "231", ANSI: "9"}, + }, + 0xFF0000, + }, + } + + for i, tc := range tt { + SetColorProfile(tc.profile) + SetHasDarkBackground(tc.darkBg) + + r, g, b, _ := tc.input.RGBA() + o := uint(r/256)<<16 + uint(g/256)<<8 + uint(b/256) + + if o != tc.expected { + t.Errorf("expected %X, got %X (test #%d)", tc.expected, o, i+1) + } + } +} From 775780f07564426f7a74ed55a7eb9069931f7c74 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Fri, 30 Sep 2022 04:40:12 +0200 Subject: [PATCH 2/2] fix: RGBA implementations for non-hex color values --- color.go | 55 ++++----------------------------------------------- color_test.go | 46 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 51 deletions(-) diff --git a/color.go b/color.go index 0e7de890..16409e16 100644 --- a/color.go +++ b/color.go @@ -1,7 +1,6 @@ package lipgloss import ( - "image/color" "sync" "github.com/muesli/termenv" @@ -144,7 +143,7 @@ func (c Color) color() termenv.Color { // // Red: 0x0, Green: 0x0, Blue: 0x0, Alpha: 0xFF. func (c Color) RGBA() (r, g, b, a uint32) { - return hexToColor(c.value()).RGBA() + return termenv.ConvertToRGB(c.color()).RGBA() } // AdaptiveColor provides color options for light and dark backgrounds. The @@ -175,8 +174,7 @@ func (ac AdaptiveColor) color() termenv.Color { // // Red: 0x0, Green: 0x0, Blue: 0x0, Alpha: 0xFF. func (ac AdaptiveColor) RGBA() (r, g, b, a uint32) { - cf := hexToColor(ac.value()) - return cf.RGBA() + return termenv.ConvertToRGB(ac.color()).RGBA() } // CompleteColor specifies exact values for truecolor, ANSI256, and ANSI color @@ -209,7 +207,7 @@ func (c CompleteColor) color() termenv.Color { // // Red: 0x0, Green: 0x0, Blue: 0x0, Alpha: 0xFFFF func (c CompleteColor) RGBA() (r, g, b, a uint32) { - return hexToColor(c.value()).RGBA() + return termenv.ConvertToRGB(c.color()).RGBA() } // CompleteColor specifies exact values for truecolor, ANSI256, and ANSI color @@ -236,50 +234,5 @@ func (cac CompleteAdaptiveColor) color() termenv.Color { // // Red: 0x0, Green: 0x0, Blue: 0x0, Alpha: 0xFFFF func (cac CompleteAdaptiveColor) RGBA() (r, g, b, a uint32) { - return hexToColor(cac.value()).RGBA() -} - -// hexToColor translates a hex color string (#RRGGBB or #RGB) into a color.RGB, -// which satisfies the color.Color interface. If an invalid string is passed -// black with 100% opacity will be returned: or, in hex format, 0x000000FF. -func hexToColor(hex string) (c color.RGBA) { - c.A = 0xFF - - if hex == "" || hex[0] != '#' { - return c - } - - const ( - fullFormat = 7 // #RRGGBB - shortFormat = 4 // #RGB - ) - - switch len(hex) { - case fullFormat: - const offset = 4 - c.R = hexToByte(hex[1])<= '0' && b <= '9': - return b - '0' - case b >= 'a' && b <= 'f': - return b - 'a' + offset - case b >= 'A' && b <= 'F': - return b - 'A' + offset - } - // Invalid, but just return 0. - return 0 + return termenv.ConvertToRGB(cac.color()).RGBA() } diff --git a/color_test.go b/color_test.go index cc0ab55c..c52dbb23 100644 --- a/color_test.go +++ b/color_test.go @@ -1,6 +1,7 @@ package lipgloss import ( + "image/color" "testing" "github.com/muesli/termenv" @@ -225,3 +226,48 @@ func TestRGBA(t *testing.T) { } } } + +// hexToColor translates a hex color string (#RRGGBB or #RGB) into a color.RGB, +// which satisfies the color.Color interface. If an invalid string is passed +// black with 100% opacity will be returned: or, in hex format, 0x000000FF. +func hexToColor(hex string) (c color.RGBA) { + c.A = 0xFF + + if hex == "" || hex[0] != '#' { + return c + } + + const ( + fullFormat = 7 // #RRGGBB + shortFormat = 4 // #RGB + ) + + switch len(hex) { + case fullFormat: + const offset = 4 + c.R = hexToByte(hex[1])<= '0' && b <= '9': + return b - '0' + case b >= 'a' && b <= 'f': + return b - 'a' + offset + case b >= 'A' && b <= 'F': + return b - 'A' + offset + } + // Invalid, but just return 0. + return 0 +}