// // Copyright (c) 2016-2020 The Aurora Authors. All rights reserved. // This program is free software. It comes without any warranty, // to the extent permitted by applicable law. You can redistribute // it and/or modify it under the terms of the Unlicense. See LICENSE // file for more details or see below. // // // This is free and unencumbered software released into the public domain. // // Anyone is free to copy, modify, publish, use, compile, sell, or // distribute this software, either in source code form or as a compiled // binary, for any purpose, commercial or non-commercial, and by any // means. // // In jurisdictions that recognize copyright laws, the author or authors // of this software dedicate any and all copyright interest in the // software to the public domain. We make this dedication for the benefit // of the public at large and to the detriment of our heirs and // successors. We intend this dedication to be an overt act of // relinquishment in perpetuity of all present and future rights to this // software under copyright law. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. // IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR // OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. // // For more information, please refer to // package aurora // A Color type is a color. It can contain // one background color, one foreground color // and a format, including ideogram related // formats. type Color uint /* Developer note. The int type is architecture depended and can be represented as int32 or int64. Thus, we can use 32-bits only to be fast and cross-platform. All supported formats requires 14 bits. It is first 14 bits. A foreground color requires 8 bit + 1 bit (presence flag). And the same for background color. The Color representations [ bg 8 bit ] [fg 8 bit ] [ fg/bg 2 bits ] [ fm 14 bits ] https://play.golang.org/p/fq2zcNstFoF */ // Special formats const ( BoldFm Color = 1 << iota // 1 FaintFm // 2 ItalicFm // 3 UnderlineFm // 4 SlowBlinkFm // 5 RapidBlinkFm // 6 ReverseFm // 7 ConcealFm // 8 CrossedOutFm // 9 FrakturFm // 20 DoublyUnderlineFm // 21 or bold off for some systems FramedFm // 51 EncircledFm // 52 OverlinedFm // 53 InverseFm = ReverseFm // alias to ReverseFm BlinkFm = SlowBlinkFm // alias to SlowBlinkFm HiddenFm = ConcealFm // alias to ConcealFm StrikeThroughFm = CrossedOutFm // alias to CrossedOutFm maskFm = BoldFm | FaintFm | ItalicFm | UnderlineFm | SlowBlinkFm | RapidBlinkFm | ReverseFm | ConcealFm | CrossedOutFm | FrakturFm | DoublyUnderlineFm | FramedFm | EncircledFm | OverlinedFm flagFg Color = 1 << 14 // presence flag (14th bit) flagBg Color = 1 << 15 // presence flag (15th bit) shiftFg = 16 // shift for foreground (starting from 16th bit) shiftBg = 24 // shift for background (starting from 24th bit) ) // Foreground colors and related formats const ( // 8 bits // [ 0; 7] - 30-37 // [ 8; 15] - 90-97 // [ 16; 231] - RGB // [232; 255] - grayscale BlackFg Color = (iota << shiftFg) | flagFg // 30, 90 RedFg // 31, 91 GreenFg // 32, 92 YellowFg // 33, 93 BlueFg // 34, 94 MagentaFg // 35, 95 CyanFg // 36, 96 WhiteFg // 37, 97 BrightFg Color = ((1 << 3) << shiftFg) | flagFg // -> 90 // the BrightFg itself doesn't represent // a color, thus it has not flagFg // 5 bits // BrownFg represents brown foreground color. // // Deprecated: use YellowFg instead, following specifications BrownFg = YellowFg // maskFg = (0xff << shiftFg) | flagFg ) // Background colors and related formats const ( // 8 bits // [ 0; 7] - 40-47 // [ 8; 15] - 100-107 // [ 16; 231] - RGB // [232; 255] - grayscale BlackBg Color = (iota << shiftBg) | flagBg // 40, 100 RedBg // 41, 101 GreenBg // 42, 102 YellowBg // 43, 103 BlueBg // 44, 104 MagentaBg // 45, 105 CyanBg // 46, 106 WhiteBg // 47, 107 BrightBg Color = ((1 << 3) << shiftBg) | flagBg // -> 100 // the BrightBg itself doesn't represent // a color, thus it has not flagBg // 5 bits // BrownBg represents brown foreground color. // // Deprecated: use YellowBg instead, following specifications BrownBg = YellowBg // maskBg = (0xff << shiftBg) | flagBg ) const ( availFlags = "-+# 0" esc = "\033[" clear = esc + "0m" ) // IsValid returns true always // // Deprecated: don't use this method anymore func (c Color) IsValid() bool { return true } // Nos returns string like 1;7;31;45. It // may be an empty string for empty color. // If the zero is true, then the string // is prepended with 0; func (c Color) Nos(zero bool) string { return string(c.appendNos(make([]byte, 0, 59), zero)) } func appendCond(bs []byte, cond, semi bool, vals ...byte) []byte { if !cond { return bs } return appendSemi(bs, semi, vals...) } // if the semi is true, then prepend with semicolon func appendSemi(bs []byte, semi bool, vals ...byte) []byte { if semi { bs = append(bs, ';') } return append(bs, vals...) } func itoa(t byte) string { var ( a [3]byte j = 2 ) for i := 0; i < 3; i, j = i+1, j-1 { a[j] = '0' + t%10 if t = t / 10; t == 0 { break } } return string(a[j:]) } func (c Color) appendFg(bs []byte, zero bool) []byte { if zero || c&maskFm != 0 { bs = append(bs, ';') } // 0- 7 : 30-37 // 8-15 : 90-97 // > 15 : 38;5;val switch fg := (c & maskFg) >> shiftFg; { case fg <= 7: // '3' and the value itself bs = append(bs, '3', '0'+byte(fg)) case fg <= 15: // '9' and the value itself bs = append(bs, '9', '0'+byte(fg&^0x08)) // clear bright flag default: bs = append(bs, '3', '8', ';', '5', ';') bs = append(bs, itoa(byte(fg))...) } return bs } func (c Color) appendBg(bs []byte, zero bool) []byte { if zero || c&(maskFm|maskFg) != 0 { bs = append(bs, ';') } // 0- 7 : 40- 47 // 8-15 : 100-107 // > 15 : 48;5;val switch fg := (c & maskBg) >> shiftBg; { case fg <= 7: // '3' and the value itself bs = append(bs, '4', '0'+byte(fg)) case fg <= 15: // '1', '0' and the value itself bs = append(bs, '1', '0', '0'+byte(fg&^0x08)) // clear bright flag default: bs = append(bs, '4', '8', ';', '5', ';') bs = append(bs, itoa(byte(fg))...) } return bs } func (c Color) appendFm9(bs []byte, zero bool) []byte { bs = appendCond(bs, c&ItalicFm != 0, zero || c&(BoldFm|FaintFm) != 0, '3') bs = appendCond(bs, c&UnderlineFm != 0, zero || c&(BoldFm|FaintFm|ItalicFm) != 0, '4') // don't combine slow and rapid blink using only // on of them, preferring slow blink if c&SlowBlinkFm != 0 { bs = appendSemi(bs, zero || c&(BoldFm|FaintFm|ItalicFm|UnderlineFm) != 0, '5') } else if c&RapidBlinkFm != 0 { bs = appendSemi(bs, zero || c&(BoldFm|FaintFm|ItalicFm|UnderlineFm) != 0, '6') } // including 1-2 const mask6i = BoldFm | FaintFm | ItalicFm | UnderlineFm | SlowBlinkFm | RapidBlinkFm bs = appendCond(bs, c&ReverseFm != 0, zero || c&(mask6i) != 0, '7') bs = appendCond(bs, c&ConcealFm != 0, zero || c&(mask6i|ReverseFm) != 0, '8') bs = appendCond(bs, c&CrossedOutFm != 0, zero || c&(mask6i|ReverseFm|ConcealFm) != 0, '9') return bs } // append 1;3;38;5;216 like string that represents ANSI // color of the Color; the zero argument requires // appending of '0' before to reset previous format // and colors func (c Color) appendNos(bs []byte, zero bool) []byte { if zero { bs = append(bs, '0') // reset previous } // formats // if c&maskFm != 0 { // 1-2 // don't combine bold and faint using only on of them, preferring bold if c&BoldFm != 0 { bs = appendSemi(bs, zero, '1') } else if c&FaintFm != 0 { bs = appendSemi(bs, zero, '2') } // 3-9 const mask9 = ItalicFm | UnderlineFm | SlowBlinkFm | RapidBlinkFm | ReverseFm | ConcealFm | CrossedOutFm if c&mask9 != 0 { bs = c.appendFm9(bs, zero) } // 20-21 const ( mask21 = FrakturFm | DoublyUnderlineFm mask9i = BoldFm | FaintFm | mask9 ) if c&mask21 != 0 { bs = appendCond(bs, c&FrakturFm != 0, zero || c&mask9i != 0, '2', '0') bs = appendCond(bs, c&DoublyUnderlineFm != 0, zero || c&(mask9i|FrakturFm) != 0, '2', '1') } // 50-53 const ( mask53 = FramedFm | EncircledFm | OverlinedFm mask21i = mask9i | mask21 ) if c&mask53 != 0 { bs = appendCond(bs, c&FramedFm != 0, zero || c&mask21i != 0, '5', '1') bs = appendCond(bs, c&EncircledFm != 0, zero || c&(mask21i|FramedFm) != 0, '5', '2') bs = appendCond(bs, c&OverlinedFm != 0, zero || c&(mask21i|FramedFm|EncircledFm) != 0, '5', '3') } } // foreground if c&maskFg != 0 { bs = c.appendFg(bs, zero) } // background if c&maskBg != 0 { bs = c.appendBg(bs, zero) } return bs }