rui_orig/color.go

192 lines
4.5 KiB
Go

package rui
import (
"bytes"
"errors"
"fmt"
"strconv"
"strings"
)
// Color - represent color in argb format
type Color uint32
// ARGB creates Color using alpha, red, green and blue components
func ARGB[T int | uint | int8 | uint8](alpha, red, green, blue T) Color {
return ((Color(alpha) & 0xFF) << 24) +
((Color(red) & 0xFF) << 16) +
((Color(green) & 0xFF) << 8) +
(Color(blue) & 0xFF)
}
// ARGB - return alpha, red, green and blue components of the color
func (color Color) ARGB() (uint8, uint8, uint8, uint8) {
return uint8(color >> 24),
uint8((color >> 16) & 0xFF),
uint8((color >> 8) & 0xFF),
uint8(color & 0xFF)
}
// Alpha - return the alpha component of the color
func (color Color) Alpha() int {
return int((color >> 24) & 0xFF)
}
// Red - return the red component of the color
func (color Color) Red() int {
return int((color >> 16) & 0xFF)
}
// Green - return the green component of the color
func (color Color) Green() int {
return int((color >> 8) & 0xFF)
}
// Blue - return the blue component of the color
func (color Color) Blue() int {
return int(color & 0xFF)
}
// String get a text representation of the color
func (color Color) String() string {
return fmt.Sprintf("#%08X", int(color))
}
func (color Color) rgbString() string {
return fmt.Sprintf("#%06X", int(color&0xFFFFFF))
}
// writeData write a text representation of the color to the buffer
func (color Color) writeData(buffer *bytes.Buffer) {
buffer.WriteString(color.String())
}
// cssString get the text representation of the color in CSS format
func (color Color) cssString() string {
red := color.Red()
green := color.Green()
blue := color.Blue()
if alpha := color.Alpha(); alpha < 255 {
aText := fmt.Sprintf("%.2f", float64(alpha)/255.0)
if len(aText) > 1 {
aText = aText[1:]
}
return fmt.Sprintf("rgba(%d,%d,%d,%s)", red, green, blue, aText)
}
return fmt.Sprintf("rgb(%d,%d,%d)", red, green, blue)
}
// StringToColor converts the string argument to Color value
func StringToColor(text string) (Color, bool) {
color, err := stringToColor(text)
if err != nil {
ErrorLog(err.Error())
return color, false
}
return color, true
}
func stringToColor(text string) (Color, error) {
text = strings.Trim(text, " \t\r\n")
if text == "" {
return 0, errors.New(`invalid color value: ""`)
}
if text[0] == '#' {
c, err := strconv.ParseUint(text[1:], 16, 32)
if err != nil {
return 0, errors.New("Set color value error: " + err.Error())
}
switch len(text) - 1 {
case 8:
return Color(c), nil
case 6:
return Color(c | 0xFF000000), nil
case 4:
a := (c >> 12) & 0xF
r := (c >> 8) & 0xF
g := (c >> 4) & 0xF
b := c & 0xF
return Color((a << 28) | (a << 24) | (r << 20) | (r << 16) | (g << 12) | (g << 8) | (b << 4) | b), nil
case 3:
r := (c >> 8) & 0xF
g := (c >> 4) & 0xF
b := c & 0xF
return Color(0xFF000000 | (r << 20) | (r << 16) | (g << 12) | (g << 8) | (b << 4) | b), nil
}
return 0, errors.New(`Invalid color format: "` + text + `". Valid formats: #AARRGGBB, #RRGGBB, #ARGB, #RGB`)
}
parseRGB := func(args string) []int {
args = strings.Trim(args, " \t")
count := len(args)
if count < 3 || args[0] != '(' || args[count-1] != ')' {
return []int{}
}
arg := strings.Split(args[1:count-1], ",")
result := make([]int, len(arg))
for i, val := range arg {
val = strings.Trim(val, " \t")
size := len(val)
if size == 0 {
return []int{}
}
if val[size-1] == '%' {
if n, err := strconv.Atoi(val[:size-1]); err == nil && n >= 0 && n <= 100 {
result[i] = n * 255 / 100
} else {
return []int{}
}
} else if strings.ContainsRune(val, '.') {
if val[0] == '.' {
val = "0" + val
}
if f, err := strconv.ParseFloat(val, 32); err == nil && f >= 0 && f <= 1 {
result[i] = int(f * 255)
} else {
return []int{}
}
} else {
if n, err := strconv.Atoi(val); err == nil && n >= 0 && n <= 255 {
result[i] = n
} else {
return []int{}
}
}
}
return result
}
text = strings.ToLower(text)
if strings.HasPrefix(text, "rgba") {
args := parseRGB(text[4:])
if len(args) == 4 {
return Color((args[3] << 24) | (args[0] << 16) | (args[1] << 8) | args[2]), nil
}
}
if strings.HasPrefix(text, "rgb") {
args := parseRGB(text[3:])
if len(args) == 3 {
return Color(0xFF000000 | (args[0] << 16) | (args[1] << 8) | args[2]), nil
}
}
// TODO hsl(360,100%,50%), hsla(360,100%,50%,.5)
if color, ok := colorConstants[text]; ok {
return color, nil
}
return 0, errors.New(`Invalid color format: "` + text + `"`)
}