notify.moe/arn/HSLColor.go

103 lines
2.3 KiB
Go

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
}