...

Source file src/github.com/muesli/termenv/color.go

Documentation: github.com/muesli/termenv

     1  package termenv
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"math"
     7  	"strings"
     8  
     9  	"github.com/lucasb-eyer/go-colorful"
    10  )
    11  
    12  var (
    13  	// ErrInvalidColor gets returned when a color is invalid.
    14  	ErrInvalidColor = errors.New("invalid color")
    15  )
    16  
    17  // Foreground and Background sequence codes
    18  const (
    19  	Foreground = "38"
    20  	Background = "48"
    21  )
    22  
    23  // Color is an interface implemented by all colors that can be converted to an
    24  // ANSI sequence.
    25  type Color interface {
    26  	// Sequence returns the ANSI Sequence for the color.
    27  	Sequence(bg bool) string
    28  }
    29  
    30  // NoColor is a nop for terminals that don't support colors.
    31  type NoColor struct{}
    32  
    33  func (c NoColor) String() string {
    34  	return ""
    35  }
    36  
    37  // ANSIColor is a color (0-15) as defined by the ANSI Standard.
    38  type ANSIColor int
    39  
    40  func (c ANSIColor) String() string {
    41  	return ansiHex[c]
    42  }
    43  
    44  // ANSI256Color is a color (16-255) as defined by the ANSI Standard.
    45  type ANSI256Color int
    46  
    47  func (c ANSI256Color) String() string {
    48  	return ansiHex[c]
    49  }
    50  
    51  // RGBColor is a hex-encoded color, e.g. "#abcdef".
    52  type RGBColor string
    53  
    54  // ConvertToRGB converts a Color to a colorful.Color.
    55  func ConvertToRGB(c Color) colorful.Color {
    56  	var hex string
    57  	switch v := c.(type) {
    58  	case RGBColor:
    59  		hex = string(v)
    60  	case ANSIColor:
    61  		hex = ansiHex[v]
    62  	case ANSI256Color:
    63  		hex = ansiHex[v]
    64  	}
    65  
    66  	ch, _ := colorful.Hex(hex)
    67  	return ch
    68  }
    69  
    70  // Sequence returns the ANSI Sequence for the color.
    71  func (c NoColor) Sequence(_ bool) string {
    72  	return ""
    73  }
    74  
    75  // Sequence returns the ANSI Sequence for the color.
    76  func (c ANSIColor) Sequence(bg bool) string {
    77  	col := int(c)
    78  	bgMod := func(c int) int {
    79  		if bg {
    80  			return c + 10
    81  		}
    82  		return c
    83  	}
    84  
    85  	if col < 8 {
    86  		return fmt.Sprintf("%d", bgMod(col)+30)
    87  	}
    88  	return fmt.Sprintf("%d", bgMod(col-8)+90)
    89  }
    90  
    91  // Sequence returns the ANSI Sequence for the color.
    92  func (c ANSI256Color) Sequence(bg bool) string {
    93  	prefix := Foreground
    94  	if bg {
    95  		prefix = Background
    96  	}
    97  	return fmt.Sprintf("%s;5;%d", prefix, c)
    98  }
    99  
   100  // Sequence returns the ANSI Sequence for the color.
   101  func (c RGBColor) Sequence(bg bool) string {
   102  	f, err := colorful.Hex(string(c))
   103  	if err != nil {
   104  		return ""
   105  	}
   106  
   107  	prefix := Foreground
   108  	if bg {
   109  		prefix = Background
   110  	}
   111  	return fmt.Sprintf("%s;2;%d;%d;%d", prefix, uint8(f.R*255), uint8(f.G*255), uint8(f.B*255))
   112  }
   113  
   114  func xTermColor(s string) (RGBColor, error) {
   115  	if len(s) < 24 || len(s) > 25 {
   116  		return RGBColor(""), ErrInvalidColor
   117  	}
   118  
   119  	switch {
   120  	case strings.HasSuffix(s, string(BEL)):
   121  		s = strings.TrimSuffix(s, string(BEL))
   122  	case strings.HasSuffix(s, string(ESC)):
   123  		s = strings.TrimSuffix(s, string(ESC))
   124  	case strings.HasSuffix(s, ST):
   125  		s = strings.TrimSuffix(s, ST)
   126  	default:
   127  		return RGBColor(""), ErrInvalidColor
   128  	}
   129  
   130  	s = s[4:]
   131  
   132  	prefix := ";rgb:"
   133  	if !strings.HasPrefix(s, prefix) {
   134  		return RGBColor(""), ErrInvalidColor
   135  	}
   136  	s = strings.TrimPrefix(s, prefix)
   137  
   138  	h := strings.Split(s, "/")
   139  	hex := fmt.Sprintf("#%s%s%s", h[0][:2], h[1][:2], h[2][:2])
   140  	return RGBColor(hex), nil
   141  }
   142  
   143  func ansi256ToANSIColor(c ANSI256Color) ANSIColor {
   144  	var r int
   145  	md := math.MaxFloat64
   146  
   147  	h, _ := colorful.Hex(ansiHex[c])
   148  	for i := 0; i <= 15; i++ {
   149  		hb, _ := colorful.Hex(ansiHex[i])
   150  		d := h.DistanceHSLuv(hb)
   151  
   152  		if d < md {
   153  			md = d
   154  			r = i
   155  		}
   156  	}
   157  
   158  	return ANSIColor(r)
   159  }
   160  
   161  func hexToANSI256Color(c colorful.Color) ANSI256Color {
   162  	v2ci := func(v float64) int {
   163  		if v < 48 {
   164  			return 0
   165  		}
   166  		if v < 115 {
   167  			return 1
   168  		}
   169  		return int((v - 35) / 40)
   170  	}
   171  
   172  	// Calculate the nearest 0-based color index at 16..231
   173  	r := v2ci(c.R * 255.0) // 0..5 each
   174  	g := v2ci(c.G * 255.0)
   175  	b := v2ci(c.B * 255.0)
   176  	ci := 36*r + 6*g + b /* 0..215 */
   177  
   178  	// Calculate the represented colors back from the index
   179  	i2cv := [6]int{0, 0x5f, 0x87, 0xaf, 0xd7, 0xff}
   180  	cr := i2cv[r] // r/g/b, 0..255 each
   181  	cg := i2cv[g]
   182  	cb := i2cv[b]
   183  
   184  	// Calculate the nearest 0-based gray index at 232..255
   185  	var grayIdx int
   186  	average := (r + g + b) / 3
   187  	if average > 238 {
   188  		grayIdx = 23
   189  	} else {
   190  		grayIdx = (average - 3) / 10 // 0..23
   191  	}
   192  	gv := 8 + 10*grayIdx // same value for r/g/b, 0..255
   193  
   194  	// Return the one which is nearer to the original input rgb value
   195  	c2 := colorful.Color{R: float64(cr) / 255.0, G: float64(cg) / 255.0, B: float64(cb) / 255.0}
   196  	g2 := colorful.Color{R: float64(gv) / 255.0, G: float64(gv) / 255.0, B: float64(gv) / 255.0}
   197  	colorDist := c.DistanceHSLuv(c2)
   198  	grayDist := c.DistanceHSLuv(g2)
   199  
   200  	if colorDist <= grayDist {
   201  		return ANSI256Color(16 + ci)
   202  	}
   203  	return ANSI256Color(232 + grayIdx)
   204  }
   205  

View as plain text