392 lines
9.3 KiB
Go
392 lines
9.3 KiB
Go
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;<r>;<g>;<b> … m // Select RGB foreground color
|
|
// ESC[ … 48;2;<r>;<g>;<b> … 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
|
|
}
|