package color import ( "fmt" "strconv" "strings" ) // 24 bit RGB color // RGB: // R 0-255 G 0-255 B 0-255 // R 00-FF G 00-FF B 00-FF (16进制) // // Format: // ESC[ … 38;2;;; … m // Select RGB foreground color // ESC[ … 48;2;;; … m // Choose RGB background color // // links: // https://zh.wikipedia.org/wiki/ANSI%E8%BD%AC%E4%B9%89%E5%BA%8F%E5%88%97#24位 // // example: // fg: \x1b[38;2;30;144;255mMESSAGE\x1b[0m // bg: \x1b[48;2;30;144;255mMESSAGE\x1b[0m // both: \x1b[38;2;233;90;203;48;2;30;144;255mMESSAGE\x1b[0m const ( TplFgRGB = "38;2;%d;%d;%d" TplBgRGB = "48;2;%d;%d;%d" FgRGBPfx = "38;2;" BgRGBPfx = "48;2;" ) // mark color is fg or bg. const ( AsFg uint8 = iota AsBg ) // values from https://github.com/go-terminfo/terminfo // var ( // RgbaBlack = image_color.RGBA{0, 0, 0, 255} // Red = color.RGBA{205, 0, 0, 255} // Green = color.RGBA{0, 205, 0, 255} // Orange = color.RGBA{205, 205, 0, 255} // Blue = color.RGBA{0, 0, 238, 255} // Magenta = color.RGBA{205, 0, 205, 255} // Cyan = color.RGBA{0, 205, 205, 255} // LightGrey = color.RGBA{229, 229, 229, 255} // // DarkGrey = color.RGBA{127, 127, 127, 255} // LightRed = color.RGBA{255, 0, 0, 255} // LightGreen = color.RGBA{0, 255, 0, 255} // Yellow = color.RGBA{255, 255, 0, 255} // LightBlue = color.RGBA{92, 92, 255, 255} // LightMagenta = color.RGBA{255, 0, 255, 255} // LightCyan = color.RGBA{0, 255, 255, 255} // White = color.RGBA{255, 255, 255, 255} // ) /************************************************************* * RGB Color(Bit24Color, TrueColor) *************************************************************/ // RGBColor definition. // // The first to third digits represent the color value. // The last digit represents the foreground(0), background(1), >1 is unset value // // Usage: // // 0, 1, 2 is R,G,B. // // 3rd: Fg=0, Bg=1, >1: unset value // RGBColor{30,144,255, 0} // RGBColor{30,144,255, 1} // // NOTICE: now support RGB color on windows CMD, PowerShell type RGBColor [4]uint8 // create a empty RGBColor var emptyRGBColor = RGBColor{3: 99} // RGB color create. // Usage: // c := RGB(30,144,255) // c := RGB(30,144,255, true) // c.Print("message") func RGB(r, g, b uint8, isBg ...bool) RGBColor { rgb := RGBColor{r, g, b} if len(isBg) > 0 && isBg[0] { rgb[3] = AsBg } return rgb } // Rgb alias of the RGB() func Rgb(r, g, b uint8, isBg ...bool) RGBColor { return RGB(r, g, b, isBg...) } // Bit24 alias of the RGB() func Bit24(r, g, b uint8, isBg ...bool) RGBColor { return RGB(r, g, b, isBg...) } // RGBFromSlice quick RGBColor from slice func RGBFromSlice(rgb []uint8, isBg ...bool) RGBColor { return RGB(rgb[0], rgb[1], rgb[2], isBg...) } // HEX create RGB color from a HEX color string. // Usage: // c := HEX("ccc") // rgb: [204 204 204] // c := HEX("aabbcc") // rgb: [170 187 204] // c := HEX("#aabbcc") // c := HEX("0xaabbcc") // c.Print("message") func HEX(hex string, isBg ...bool) RGBColor { if rgb := HexToRgb(hex); len(rgb) > 0 { return RGB(uint8(rgb[0]), uint8(rgb[1]), uint8(rgb[2]), isBg...) } // mark is empty return emptyRGBColor } // Hex alias of the HEX() func Hex(hex string, isBg ...bool) RGBColor { return HEX(hex, isBg...) } // RGBFromString create RGB color from a string. // Usage: // c := RGBFromString("170,187,204") // c.Print("message") func RGBFromString(rgb string, isBg ...bool) RGBColor { ss := stringToArr(rgb, ",") if len(ss) != 3 { return emptyRGBColor } var ar [3]int for i, val := range ss { iv, err := strconv.Atoi(val) if err != nil { return emptyRGBColor } ar[i] = iv } return RGB(uint8(ar[0]), uint8(ar[1]), uint8(ar[2]), isBg...) } // Set terminal by rgb/true color code func (c RGBColor) Set() error { return SetTerminal(c.String()) } // Reset terminal. alias of the ResetTerminal() func (c RGBColor) Reset() error { return ResetTerminal() } // Print print message func (c RGBColor) Print(a ...interface{}) { doPrintV2(c.String(), fmt.Sprint(a...)) } // Printf format and print message func (c RGBColor) Printf(format string, a ...interface{}) { doPrintV2(c.String(), fmt.Sprintf(format, a...)) } // Println print message with newline func (c RGBColor) Println(a ...interface{}) { doPrintlnV2(c.String(), a) } // Sprint returns rendered message func (c RGBColor) Sprint(a ...interface{}) string { return RenderCode(c.String(), a...) } // Sprintf returns format and rendered message func (c RGBColor) Sprintf(format string, a ...interface{}) string { return RenderString(c.String(), fmt.Sprintf(format, a...)) } // Values to RGB values func (c RGBColor) Values() []int { return []int{int(c[0]), int(c[1]), int(c[2])} } // Code to color code string without prefix. eg: "204;123;56" func (c RGBColor) Code() string { return fmt.Sprintf("%d;%d;%d", c[0], c[1], c[2]) } // Hex color rgb to hex string. as in "ff0080". func (c RGBColor) Hex() string { return fmt.Sprintf("%02x%02x%02x", c[0], c[1], c[2]) } // FullCode to color code string with prefix func (c RGBColor) FullCode() string { return c.String() } // String to color code string with prefix. eg: "38;2;204;123;56" func (c RGBColor) String() string { if c[3] == AsFg { return fmt.Sprintf(TplFgRGB, c[0], c[1], c[2]) } if c[3] == AsBg { return fmt.Sprintf(TplBgRGB, c[0], c[1], c[2]) } // c[3] > 1 is empty return "" } // IsEmpty value func (c RGBColor) IsEmpty() bool { return c[3] > AsBg } // IsValid value // func (c RGBColor) IsValid() bool { // return c[3] <= AsBg // } // C256 returns the closest approximate 256 (8 bit) color func (c RGBColor) C256() Color256 { return C256(RgbTo256(c[0], c[1], c[2]), c[3] == AsBg) } // Basic returns the closest approximate 16 (4 bit) color func (c RGBColor) Basic() Color { // return Color(RgbToAnsi(c[0], c[1], c[2], c[3] == AsBg)) return Color(Rgb2basic(c[0], c[1], c[2], c[3] == AsBg)) } // Color returns the closest approximate 16 (4 bit) color func (c RGBColor) Color() Color { return c.Basic() } // C16 returns the closest approximate 16 (4 bit) color func (c RGBColor) C16() Color { return c.Basic() } /************************************************************* * RGB Style *************************************************************/ // RGBStyle definition. // // Foreground/Background color // All are composed of 4 digits uint8, the first three digits are the color value; // The last bit is different from RGBColor, here it indicates whether the value is set. // - 1 Has been set // - ^1 Not set type RGBStyle struct { // Name of the style Name string // color options of the style opts Opts // fg and bg color fg, bg RGBColor } // NewRGBStyle create a RGBStyle. func NewRGBStyle(fg RGBColor, bg ...RGBColor) *RGBStyle { s := &RGBStyle{} if len(bg) > 0 { s.SetBg(bg[0]) } return s.SetFg(fg) } // HEXStyle create a RGBStyle from HEX color string. // Usage: // s := HEXStyle("aabbcc", "eee") // s.Print("message") func HEXStyle(fg string, bg ...string) *RGBStyle { s := &RGBStyle{} if len(bg) > 0 { s.SetBg(HEX(bg[0])) } if len(fg) > 0 { s.SetFg(HEX(fg)) } return s } // RGBStyleFromString create a RGBStyle from color value string. // Usage: // s := RGBStyleFromString("170,187,204", "70,87,4") // s.Print("message") func RGBStyleFromString(fg string, bg ...string) *RGBStyle { s := &RGBStyle{} if len(bg) > 0 { s.SetBg(RGBFromString(bg[0])) } return s.SetFg(RGBFromString(fg)) } // Set fg and bg color, can also with color options func (s *RGBStyle) Set(fg, bg RGBColor, opts ...Color) *RGBStyle { return s.SetFg(fg).SetBg(bg).SetOpts(opts) } // SetFg set fg color func (s *RGBStyle) SetFg(fg RGBColor) *RGBStyle { fg[3] = 1 // add fixed value, mark is valid s.fg = fg return s } // SetBg set bg color func (s *RGBStyle) SetBg(bg RGBColor) *RGBStyle { bg[3] = 1 // add fixed value, mark is valid s.bg = bg return s } // SetOpts set color options func (s *RGBStyle) SetOpts(opts Opts) *RGBStyle { s.opts = opts return s } // AddOpts add options func (s *RGBStyle) AddOpts(opts ...Color) *RGBStyle { s.opts.Add(opts...) return s } // Print print message func (s *RGBStyle) Print(a ...interface{}) { doPrintV2(s.String(), fmt.Sprint(a...)) } // Printf format and print message func (s *RGBStyle) Printf(format string, a ...interface{}) { doPrintV2(s.String(), fmt.Sprintf(format, a...)) } // Println print message with newline func (s *RGBStyle) Println(a ...interface{}) { doPrintlnV2(s.String(), a) } // Sprint returns rendered message func (s *RGBStyle) Sprint(a ...interface{}) string { return RenderCode(s.String(), a...) } // Sprintf returns format and rendered message func (s *RGBStyle) Sprintf(format string, a ...interface{}) string { return RenderString(s.String(), fmt.Sprintf(format, a...)) } // Code convert to color code string func (s *RGBStyle) Code() string { return s.String() } // FullCode convert to color code string func (s *RGBStyle) FullCode() string { return s.String() } // String convert to color code string func (s *RGBStyle) String() string { var ss []string // last value ensure is enable. if s.fg[3] == 1 { ss = append(ss, fmt.Sprintf(TplFgRGB, s.fg[0], s.fg[1], s.fg[2])) } if s.bg[3] == 1 { ss = append(ss, fmt.Sprintf(TplBgRGB, s.bg[0], s.bg[1], s.bg[2])) } if s.opts.IsValid() { ss = append(ss, s.opts.String()) } return strings.Join(ss, ";") } // IsEmpty style func (s *RGBStyle) IsEmpty() bool { return s.fg[3] != 1 && s.bg[3] != 1 }