You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
hexmap/server/state/hexcolor.go

105 lines
2.8 KiB

package state
import "fmt"
// HexColor is an internal representation of a hexadecimal color string.
// It can take one of these formats:
// #RRGGBBAA
// #RRGGBB
// #RGBA
// #RGB
// When marshaling, it will always choose the most efficient format, but any format can be used when unmarshaling.
type HexColor 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
}
// HexColorFromString decodes a hexadecimal string into a hex color.
func HexColorFromString(text string) (HexColor, error) {
var hex HexColor
if err := (&hex).UnmarshalText([]byte(text)); err != nil {
return hex, err
}
return hex, nil
}
func (h *HexColor) UnmarshalText(text []byte) error {
var count, expected int
var short bool
var scanErr error
switch len(text) {
case 9:
// Long form with alpha: #RRGGBBAA
expected = 4
short = false
count, scanErr = fmt.Sscanf(string(text), "#%02X%02X%02X%02X", &h.R, &h.G, &h.B, &h.A)
case 7:
// Long form: #RRGGBB
expected = 3
h.A = 0xFF
count, scanErr = fmt.Sscanf(string(text), "#%02X%02X%02X", &h.R, &h.G, &h.B)
case 5:
// Short form with alpha: #RGBA
expected = 4
short = true
count, scanErr = fmt.Sscanf(string(text), "#%01X%01X%01X%01X", &h.R, &h.G, &h.B, &h.A)
case 4:
// Short form: #RGB
expected = 3
h.A = 0xF
short = true
count, scanErr = fmt.Sscanf(string(text), "#%01X%01X%01X", &h.R, &h.G, &h.B)
default:
return fmt.Errorf("can't decode %s as HexColor: wrong length", text)
}
if scanErr != nil {
return scanErr
}
if count != expected {
return fmt.Errorf("can't decode %s as HexColor: missing components", text)
}
if short {
h.R *= 0x11
h.G *= 0x11
h.B *= 0x11
h.A *= 0x11
}
return nil
}
// MarshalText marshals the HexColor into a small string.
func (h HexColor) MarshalText() (text []byte, err error) {
return []byte(h.String()), nil
}
// String prints the HexColor as 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 (h HexColor) String() string {
if h.R%0x11 == 0 && h.G%0x11 == 0 && h.B%0x11 == 0 && h.A%0x11 == 0 {
// Short form works.
if h.A == 0xFF {
// It's great when it's easy!
return fmt.Sprintf("#%01X%01X%01X", h.R/0x11, h.G/0x11, h.B/0x11)
} else {
// Just need to add the alpha.
return fmt.Sprintf("#%01X%01X%01X%01X", h.R/0x11, h.G/0x11, h.B/0x11, h.A/0x11)
}
} else {
// Gotta use long form.
if h.A == 0xFF {
// Can skip the alpha channel, though.
return fmt.Sprintf("#%02X%02X%02X", h.R, h.G, h.B)
} else {
// Doing things the hard way.
return fmt.Sprintf("#%02X%02X%02X%02X", h.R, h.G, h.B, h.A)
}
}
}