package state import ( "errors" "fmt" "strconv" "strings" ) // Color is an internal representation of an RGBA8888 color. type Color struct { // R is the red component of the color. R uint8 // G is the green component of the color. G uint8 // B is the blue component of the color. B uint8 // A is the alpha component of the color. A uint8 } var TransparentColor = Color{R: 0, G: 0, B: 0, A: 0} var ErrInvalidColorString = errors.New("color strings must start with # and be followed by 3, 4, 6, or 8 hex digits") func ColorFromString(s string) (Color, error) { if !strings.HasPrefix(s, "#") { return TransparentColor, ErrInvalidColorString } hex := s[1:] v, err := strconv.ParseUint(hex, 16, 64) if err != nil { return TransparentColor, ErrInvalidColorString } switch len(hex) { case 3: return ColorFromRGBA4444(uint16(v<<4 | 0xF)), nil case 4: return ColorFromRGBA4444(uint16(v)), nil case 6: return ColorFromRGBA8888(uint32(v<<8 | 0xFF)), nil case 8: return ColorFromRGBA8888(uint32(v)), nil default: return TransparentColor, ErrInvalidColorString } } // String prints the Color in an abbreviated notation. // Specifically, short form is used when possible (i.e., when all components are evenly divisible by 0x11). // The alpha component is left out if it's 0xFF. func (c Color) String() string { if c.R%0x11 == 0 && c.G%0x11 == 0 && c.B%0x11 == 0 && c.A%0x11 == 0 { // Short form works. if c.A == 0xFF { // It's great when it's easy! return fmt.Sprintf("#%01X%01X%01X", c.R/0x11, c.G/0x11, c.B/0x11) } else { // Just need to add the alpha. return fmt.Sprintf("#%01X%01X%01X%01X", c.R/0x11, c.G/0x11, c.B/0x11, c.A/0x11) } } else { // Gotta use long form. if c.A == 0xFF { // Can skip the alpha channel, though. return fmt.Sprintf("#%02X%02X%02X", c.R, c.G, c.B) } else { // Doing things the hard way. return fmt.Sprintf("#%02X%02X%02X%02X", c.R, c.G, c.B, c.A) } } }