diff --git a/colors.go b/colors.go new file mode 100644 index 0000000..9e12107 --- /dev/null +++ b/colors.go @@ -0,0 +1,66 @@ +package zntg + +import ( + "errors" + "image/color" + "regexp" +) + +var hexColorRE = regexp.MustCompile(`^#?([0-9A-Fa-f]{2})-?([0-9A-Fa-f]{2})-?([0-9A-Fa-f]{2})-?([0-9A-Fa-f]{2})?$`) + +// HexColor parses hexadecimal string s and returns the RGBA color. Accepted are 3 or 4 components (R, G, B, A) following an optional hash character '#'. +func HexColor(s string) (color.RGBA, error) { + match := hexColorRE.FindStringSubmatch(s) + if match == nil { + return color.RGBA{}, errors.New("invalid color format") + } + values, err := HexToInts(match[1:]...) + if err != nil { + return color.RGBA{}, err + } + a := 255 + if len(match[4]) > 0 { + a = values[3] + } + return color.RGBA{R: uint8(values[0]), G: uint8(values[1]), B: uint8(values[2]), A: uint8(a)}, nil +} + +// HexToInt tries to convert a string with hexadecimal characters to an integer. +func HexToInt(s string) (int, error) { + var i int + for _, c := range s { + i *= 16 + if c >= '0' && c <= '9' { + i += int(c - '0') + } else if c >= 'A' && c <= 'F' { + i += int(c - 'A' + 10) + } else if c >= 'a' && c <= 'f' { + i += int(c - 'a' + 10) + } else { + return 0, errors.New("hex digit not supported") + } + } + return i, nil +} + +// HexToInts tries to convert multiple strings with hexadecimal characters to a slice of integers. +func HexToInts(s ...string) ([]int, error) { + ints := make([]int, len(s)) + for i, s := range s { + value, err := HexToInt(s) + if err != nil { + return nil, err + } + ints[i] = value + } + return ints, nil +} + +// MustHexColor parses hexadecimal string s and returns the RGBA color. If the conversion fails it panics. +func MustHexColor(s string) color.RGBA { + color, err := HexColor(s) + if err != nil { + panic(err) + } + return color +}