2019-06-03 09:32:43 +00:00
|
|
|
package arn
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"image"
|
|
|
|
"math"
|
|
|
|
)
|
|
|
|
|
2019-09-10 00:49:51 +00:00
|
|
|
// HSLColor represents a color in the HSL format.
|
2019-06-03 09:32:43 +00:00
|
|
|
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
|
|
|
|
}
|