diff --git a/internal/color.go b/internal/color.go index e8f0c4e..41abd7c 100644 --- a/internal/color.go +++ b/internal/color.go @@ -1,5 +1,6 @@ package internal +import "fmt" import "image/color" // HSV represents a color with hue, saturation, and value components. Each @@ -173,3 +174,58 @@ func rgbToHSV (r, g, b uint32) HSV { return hsv } + +// FormatNRGBA formats an NRGBA value into a hex string. +func FormatNRGBA (nrgba color.NRGBA) string { + return fmt.Sprintf("%02X%02X%02X%02X", nrgba.R, nrgba.G, nrgba.B, nrgba.A) +} + +// ParseNRGBA parses an NRGBA value from a hex string. It can be of the format: +// - RGB +// - RGBA +// - RRGGBB +// - RRGGBBAA +// If none of these are specified, this function will return an opaque black +// color. Hex digits may either be upper case or lower case. +func ParseNRGBA (str string) color.NRGBA { + runes := []rune(str) + c := color.NRGBA { A: 255 } + switch len(runes) { + case 3: + c.R = fillOctet(hexDigit(runes[0])) + c.G = fillOctet(hexDigit(runes[1])) + c.B = fillOctet(hexDigit(runes[2])) + case 4: + c.R = fillOctet(hexDigit(runes[0])) + c.G = fillOctet(hexDigit(runes[1])) + c.B = fillOctet(hexDigit(runes[2])) + c.A = fillOctet(hexDigit(runes[3])) + case 6: + c.R = hexOctet(runes[0], runes[1]) + c.G = hexOctet(runes[2], runes[3]) + c.B = hexOctet(runes[4], runes[5]) + case 8: + c.R = hexOctet(runes[0], runes[1]) + c.G = hexOctet(runes[2], runes[3]) + c.B = hexOctet(runes[4], runes[5]) + c.A = hexOctet(runes[6], runes[7]) + } + return c +} + +func hexDigit (r rune) uint8 { + switch { + case r >= '0' && r <= '9': return uint8(r - '0') + case r >= 'A' && r <= 'F': return uint8(r - 'A') + 10 + case r >= 'a' && r <= 'f': return uint8(r - 'a') + 10 + default: return 0 + } +} + +func fillOctet (low uint8) uint8 { + return low << 4 | low +} + +func hexOctet (high, low rune) uint8 { + return hexDigit(high) << 4 | hexDigit(low) +}