package arn import ( "fmt" "image" "math" ) // HSLColor represents a color in the HSL format. type HSLColor struct { Hue float64 `json:"hue"` Saturation float64 `json:"saturation"` Lightness float64 `json:"lightness"` } // String returns a representation like hsl(0, 0%, 0%). func (color HSLColor) String() string { return fmt.Sprintf("hsl(%.1f, %.1f%%, %.1f%%)", color.Hue*360, color.Saturation*100, color.Lightness*100) } // StringWithAlpha returns a representation like hsla(0, 0%, 0%, 0.5). func (color HSLColor) StringWithAlpha(alpha float64) string { return fmt.Sprintf("hsla(%.1f, %.1f%%, %.1f%%, %.2f)", color.Hue*360, color.Saturation*100, color.Lightness*100, alpha) } // GetAverageColor returns the average color of an image in HSL format. func GetAverageColor(img image.Image) HSLColor { width := img.Bounds().Dx() height := img.Bounds().Dy() totalR := uint64(0) totalG := uint64(0) totalB := uint64(0) for x := 0; x < width; x++ { for y := 0; y < height; y++ { r, g, b, _ := img.At(x, y).RGBA() totalR += uint64(r) totalG += uint64(g) totalB += uint64(b) } } pixels := uint64(width * height) const max = float64(65535) averageR := float64(totalR/pixels) / max averageG := float64(totalG/pixels) / max averageB := float64(totalB/pixels) / max h, s, l := RGBToHSL(averageR, averageG, averageB) return HSLColor{h, s, l} } // RGBToHSL converts RGB to HSL (RGB input and HSL output are floats in the 0..1 range). // Original source: https://github.com/gerow/go-color func RGBToHSL(r, g, b float64) (h, s, l float64) { max := math.Max(math.Max(r, g), b) min := math.Min(math.Min(r, g), b) // Luminosity is the average of the max and min rgb color intensities. l = (max + min) / 2 // Saturation delta := max - min if delta == 0 { // It's gray return 0, 0, l } // It's not gray if l < 0.5 { s = delta / (max + min) } else { s = delta / (2 - max - min) } // Hue r2 := (((max - r) / 6) + (delta / 2)) / delta g2 := (((max - g) / 6) + (delta / 2)) / delta b2 := (((max - b) / 6) + (delta / 2)) / delta switch { case r == max: h = b2 - g2 case g == max: h = (1.0 / 3.0) + r2 - b2 case b == max: h = (2.0 / 3.0) + g2 - r2 } // fix wraparounds switch { case h < 0: h++ case h > 1: h-- } return h, s, l }