1package color 2 3import ( 4 "fmt" 5 "strconv" 6 "strings" 7) 8 9// 24 bit RGB color 10// RGB: 11// R 0-255 G 0-255 B 0-255 12// R 00-FF G 00-FF B 00-FF (16进制) 13// 14// Format: 15// ESC[ … 38;2;<r>;<g>;<b> … m // Select RGB foreground color 16// ESC[ … 48;2;<r>;<g>;<b> … m // Choose RGB background color 17// 18// links: 19// https://zh.wikipedia.org/wiki/ANSI%E8%BD%AC%E4%B9%89%E5%BA%8F%E5%88%97#24位 20// 21// example: 22// fg: \x1b[38;2;30;144;255mMESSAGE\x1b[0m 23// bg: \x1b[48;2;30;144;255mMESSAGE\x1b[0m 24// both: \x1b[38;2;233;90;203;48;2;30;144;255mMESSAGE\x1b[0m 25const ( 26 TplFgRGB = "38;2;%d;%d;%d" 27 TplBgRGB = "48;2;%d;%d;%d" 28 FgRGBPfx = "38;2;" 29 BgRGBPfx = "48;2;" 30) 31 32// mark color is fg or bg. 33const ( 34 AsFg uint8 = iota 35 AsBg 36) 37 38// values from https://github.com/go-terminfo/terminfo 39// var ( 40// RgbaBlack = image_color.RGBA{0, 0, 0, 255} 41// Red = color.RGBA{205, 0, 0, 255} 42// Green = color.RGBA{0, 205, 0, 255} 43// Orange = color.RGBA{205, 205, 0, 255} 44// Blue = color.RGBA{0, 0, 238, 255} 45// Magenta = color.RGBA{205, 0, 205, 255} 46// Cyan = color.RGBA{0, 205, 205, 255} 47// LightGrey = color.RGBA{229, 229, 229, 255} 48// 49// DarkGrey = color.RGBA{127, 127, 127, 255} 50// LightRed = color.RGBA{255, 0, 0, 255} 51// LightGreen = color.RGBA{0, 255, 0, 255} 52// Yellow = color.RGBA{255, 255, 0, 255} 53// LightBlue = color.RGBA{92, 92, 255, 255} 54// LightMagenta = color.RGBA{255, 0, 255, 255} 55// LightCyan = color.RGBA{0, 255, 255, 255} 56// White = color.RGBA{255, 255, 255, 255} 57// ) 58 59/************************************************************* 60 * RGB Color(Bit24Color, TrueColor) 61 *************************************************************/ 62 63// RGBColor definition. 64// 65// The first to third digits represent the color value. 66// The last digit represents the foreground(0), background(1), >1 is unset value 67// 68// Usage: 69// // 0, 1, 2 is R,G,B. 70// // 3rd: Fg=0, Bg=1, >1: unset value 71// RGBColor{30,144,255, 0} 72// RGBColor{30,144,255, 1} 73// 74// NOTICE: now support RGB color on windows CMD, PowerShell 75type RGBColor [4]uint8 76 77// create a empty RGBColor 78var emptyRGBColor = RGBColor{3: 99} 79 80// RGB color create. 81// Usage: 82// c := RGB(30,144,255) 83// c := RGB(30,144,255, true) 84// c.Print("message") 85func RGB(r, g, b uint8, isBg ...bool) RGBColor { 86 rgb := RGBColor{r, g, b} 87 if len(isBg) > 0 && isBg[0] { 88 rgb[3] = AsBg 89 } 90 91 return rgb 92} 93 94// Rgb alias of the RGB() 95func Rgb(r, g, b uint8, isBg ...bool) RGBColor { return RGB(r, g, b, isBg...) } 96 97// Bit24 alias of the RGB() 98func Bit24(r, g, b uint8, isBg ...bool) RGBColor { return RGB(r, g, b, isBg...) } 99 100// RGBFromSlice quick RGBColor from slice 101func RGBFromSlice(rgb []uint8, isBg ...bool) RGBColor { 102 return RGB(rgb[0], rgb[1], rgb[2], isBg...) 103} 104 105// HEX create RGB color from a HEX color string. 106// Usage: 107// c := HEX("ccc") // rgb: [204 204 204] 108// c := HEX("aabbcc") // rgb: [170 187 204] 109// c := HEX("#aabbcc") 110// c := HEX("0xaabbcc") 111// c.Print("message") 112func HEX(hex string, isBg ...bool) RGBColor { 113 if rgb := HexToRgb(hex); len(rgb) > 0 { 114 return RGB(uint8(rgb[0]), uint8(rgb[1]), uint8(rgb[2]), isBg...) 115 } 116 117 // mark is empty 118 return emptyRGBColor 119} 120 121// Hex alias of the HEX() 122func Hex(hex string, isBg ...bool) RGBColor { return HEX(hex, isBg...) } 123 124// RGBFromString create RGB color from a string. 125// Usage: 126// c := RGBFromString("170,187,204") 127// c.Print("message") 128func RGBFromString(rgb string, isBg ...bool) RGBColor { 129 ss := stringToArr(rgb, ",") 130 if len(ss) != 3 { 131 return emptyRGBColor 132 } 133 134 var ar [3]int 135 for i, val := range ss { 136 iv, err := strconv.Atoi(val) 137 if err != nil { 138 return emptyRGBColor 139 } 140 141 ar[i] = iv 142 } 143 144 return RGB(uint8(ar[0]), uint8(ar[1]), uint8(ar[2]), isBg...) 145} 146 147// Set terminal by rgb/true color code 148func (c RGBColor) Set() error { 149 return SetTerminal(c.String()) 150} 151 152// Reset terminal. alias of the ResetTerminal() 153func (c RGBColor) Reset() error { 154 return ResetTerminal() 155} 156 157// Print print message 158func (c RGBColor) Print(a ...interface{}) { 159 doPrintV2(c.String(), fmt.Sprint(a...)) 160} 161 162// Printf format and print message 163func (c RGBColor) Printf(format string, a ...interface{}) { 164 doPrintV2(c.String(), fmt.Sprintf(format, a...)) 165} 166 167// Println print message with newline 168func (c RGBColor) Println(a ...interface{}) { 169 doPrintlnV2(c.String(), a) 170} 171 172// Sprint returns rendered message 173func (c RGBColor) Sprint(a ...interface{}) string { 174 return RenderCode(c.String(), a...) 175} 176 177// Sprintf returns format and rendered message 178func (c RGBColor) Sprintf(format string, a ...interface{}) string { 179 return RenderString(c.String(), fmt.Sprintf(format, a...)) 180} 181 182// Values to RGB values 183func (c RGBColor) Values() []int { 184 return []int{int(c[0]), int(c[1]), int(c[2])} 185} 186 187// Code to color code string without prefix. eg: "204;123;56" 188func (c RGBColor) Code() string { 189 return fmt.Sprintf("%d;%d;%d", c[0], c[1], c[2]) 190} 191 192// Hex color rgb to hex string. as in "ff0080". 193func (c RGBColor) Hex() string { 194 return fmt.Sprintf("%02x%02x%02x", c[0], c[1], c[2]) 195} 196 197// FullCode to color code string with prefix 198func (c RGBColor) FullCode() string { 199 return c.String() 200} 201 202// String to color code string with prefix. eg: "38;2;204;123;56" 203func (c RGBColor) String() string { 204 if c[3] == AsFg { 205 return fmt.Sprintf(TplFgRGB, c[0], c[1], c[2]) 206 } 207 208 if c[3] == AsBg { 209 return fmt.Sprintf(TplBgRGB, c[0], c[1], c[2]) 210 } 211 212 // c[3] > 1 is empty 213 return "" 214} 215 216// IsEmpty value 217func (c RGBColor) IsEmpty() bool { 218 return c[3] > AsBg 219} 220 221// IsValid value 222// func (c RGBColor) IsValid() bool { 223// return c[3] <= AsBg 224// } 225 226// C256 returns the closest approximate 256 (8 bit) color 227func (c RGBColor) C256() Color256 { 228 return C256(RgbTo256(c[0], c[1], c[2]), c[3] == AsBg) 229} 230 231// Basic returns the closest approximate 16 (4 bit) color 232func (c RGBColor) Basic() Color { 233 // return Color(RgbToAnsi(c[0], c[1], c[2], c[3] == AsBg)) 234 return Color(Rgb2basic(c[0], c[1], c[2], c[3] == AsBg)) 235} 236 237// Color returns the closest approximate 16 (4 bit) color 238func (c RGBColor) Color() Color { return c.Basic() } 239 240// C16 returns the closest approximate 16 (4 bit) color 241func (c RGBColor) C16() Color { return c.Basic() } 242 243/************************************************************* 244 * RGB Style 245 *************************************************************/ 246 247// RGBStyle definition. 248// 249// Foreground/Background color 250// All are composed of 4 digits uint8, the first three digits are the color value; 251// The last bit is different from RGBColor, here it indicates whether the value is set. 252// - 1 Has been set 253// - ^1 Not set 254type RGBStyle struct { 255 // Name of the style 256 Name string 257 // color options of the style 258 opts Opts 259 // fg and bg color 260 fg, bg RGBColor 261} 262 263// NewRGBStyle create a RGBStyle. 264func NewRGBStyle(fg RGBColor, bg ...RGBColor) *RGBStyle { 265 s := &RGBStyle{} 266 if len(bg) > 0 { 267 s.SetBg(bg[0]) 268 } 269 270 return s.SetFg(fg) 271} 272 273// HEXStyle create a RGBStyle from HEX color string. 274// Usage: 275// s := HEXStyle("aabbcc", "eee") 276// s.Print("message") 277func HEXStyle(fg string, bg ...string) *RGBStyle { 278 s := &RGBStyle{} 279 if len(bg) > 0 { 280 s.SetBg(HEX(bg[0])) 281 } 282 283 if len(fg) > 0 { 284 s.SetFg(HEX(fg)) 285 } 286 287 return s 288} 289 290// RGBStyleFromString create a RGBStyle from color value string. 291// Usage: 292// s := RGBStyleFromString("170,187,204", "70,87,4") 293// s.Print("message") 294func RGBStyleFromString(fg string, bg ...string) *RGBStyle { 295 s := &RGBStyle{} 296 if len(bg) > 0 { 297 s.SetBg(RGBFromString(bg[0])) 298 } 299 300 return s.SetFg(RGBFromString(fg)) 301} 302 303// Set fg and bg color, can also with color options 304func (s *RGBStyle) Set(fg, bg RGBColor, opts ...Color) *RGBStyle { 305 return s.SetFg(fg).SetBg(bg).SetOpts(opts) 306} 307 308// SetFg set fg color 309func (s *RGBStyle) SetFg(fg RGBColor) *RGBStyle { 310 fg[3] = 1 // add fixed value, mark is valid 311 s.fg = fg 312 return s 313} 314 315// SetBg set bg color 316func (s *RGBStyle) SetBg(bg RGBColor) *RGBStyle { 317 bg[3] = 1 // add fixed value, mark is valid 318 s.bg = bg 319 return s 320} 321 322// SetOpts set color options 323func (s *RGBStyle) SetOpts(opts Opts) *RGBStyle { 324 s.opts = opts 325 return s 326} 327 328// AddOpts add options 329func (s *RGBStyle) AddOpts(opts ...Color) *RGBStyle { 330 s.opts.Add(opts...) 331 return s 332} 333 334// Print print message 335func (s *RGBStyle) Print(a ...interface{}) { 336 doPrintV2(s.String(), fmt.Sprint(a...)) 337} 338 339// Printf format and print message 340func (s *RGBStyle) Printf(format string, a ...interface{}) { 341 doPrintV2(s.String(), fmt.Sprintf(format, a...)) 342} 343 344// Println print message with newline 345func (s *RGBStyle) Println(a ...interface{}) { 346 doPrintlnV2(s.String(), a) 347} 348 349// Sprint returns rendered message 350func (s *RGBStyle) Sprint(a ...interface{}) string { 351 return RenderCode(s.String(), a...) 352} 353 354// Sprintf returns format and rendered message 355func (s *RGBStyle) Sprintf(format string, a ...interface{}) string { 356 return RenderString(s.String(), fmt.Sprintf(format, a...)) 357} 358 359// Code convert to color code string 360func (s *RGBStyle) Code() string { 361 return s.String() 362} 363 364// FullCode convert to color code string 365func (s *RGBStyle) FullCode() string { 366 return s.String() 367} 368 369// String convert to color code string 370func (s *RGBStyle) String() string { 371 var ss []string 372 // last value ensure is enable. 373 if s.fg[3] == 1 { 374 ss = append(ss, fmt.Sprintf(TplFgRGB, s.fg[0], s.fg[1], s.fg[2])) 375 } 376 377 if s.bg[3] == 1 { 378 ss = append(ss, fmt.Sprintf(TplBgRGB, s.bg[0], s.bg[1], s.bg[2])) 379 } 380 381 if s.opts.IsValid() { 382 ss = append(ss, s.opts.String()) 383 } 384 385 return strings.Join(ss, ";") 386} 387 388// IsEmpty style 389func (s *RGBStyle) IsEmpty() bool { 390 return s.fg[3] != 1 && s.bg[3] != 1 391} 392