colorspec.go 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. package rgb
  2. import (
  3. "fmt"
  4. "strconv"
  5. "strings"
  6. )
  7. // HSV is a color in the units the keyboard uses: hue, saturation and value,
  8. // each 0-255. The keyboard stores hue and saturation only; Value is the
  9. // brightness register, which is why it is tracked apart from the color.
  10. type HSV struct {
  11. H, S, V uint8
  12. // hasValue separates "the notation carried no value" from "the notation
  13. // asked for value 0". The hex forms mean the first, and leave V at zero.
  14. hasValue bool
  15. }
  16. // HasValue reports whether the notation carried a value component, that is,
  17. // whether the caller asked for a brightness as well as a color.
  18. func (c HSV) HasValue() bool { return c.hasValue }
  19. // ParseColorSpec parses one of the three documented color notations: a bare or
  20. // rgb:-prefixed six digit hex value, or hsv:<h>,<s>,<v> in the firmware's own
  21. // 0-255 units. A hex value contributes hue and saturation; the value component
  22. // of a hex color is not a brightness request and is dropped.
  23. func ParseColorSpec(spec string) (HSV, error) {
  24. prefix, body, found := strings.Cut(spec, ":")
  25. if !found {
  26. if prefix == "hsv" || prefix == "rgb" {
  27. return HSV{}, unknownNotation(spec)
  28. }
  29. return hexToHSV(spec)
  30. }
  31. switch prefix {
  32. case "hsv":
  33. return parseHSVSpec(body)
  34. case "rgb":
  35. return hexToHSV(body)
  36. default:
  37. return HSV{}, unknownNotation(spec)
  38. }
  39. }
  40. // hexToHSV converts a hex color to the hue and saturation the keyboard stores.
  41. // The value component of the hex color is not carried over: only the hsv
  42. // notation asks for a brightness, and HasValue keeps that distinction visible.
  43. func hexToHSV(hex string) (HSV, error) {
  44. c, err := ParseHexColor(hex)
  45. if err != nil {
  46. return HSV{}, err
  47. }
  48. h, s, _ := c.HSV()
  49. return HSV{H: h, S: s}, nil
  50. }
  51. // parseHSVSpec parses the body of an hsv: notation. Three components are
  52. // required: a two component form would have to mean "leave the brightness
  53. // alone", which no other notation does.
  54. func parseHSVSpec(body string) (HSV, error) {
  55. parts := strings.Split(body, ",")
  56. if len(parts) != 3 {
  57. return HSV{}, fmt.Errorf("want hsv:<h>,<s>,<v> with three components in 0-255, got %q", body)
  58. }
  59. var components [3]uint8
  60. for i, part := range parts {
  61. n, err := strconv.ParseUint(part, 10, 8)
  62. if err != nil {
  63. return HSV{}, fmt.Errorf("invalid hsv component %q: want 0-255", part)
  64. }
  65. components[i] = uint8(n)
  66. }
  67. return HSV{H: components[0], S: components[1], V: components[2], hasValue: true}, nil
  68. }
  69. // unknownNotation names the accepted forms, because a rejected notation is
  70. // usually a typo of one of them.
  71. func unknownNotation(spec string) error {
  72. return fmt.Errorf("unknown color notation %q: use ff0000, rgb:ff0000 or hsv:0,255,255", spec)
  73. }