mirror of
https://github.com/gohugoio/hugo.git
synced 2025-08-29 22:29:56 +02:00
resources/images: Add $image.Colors
Which returns the most dominant colors of an image using a simple histogram method. Fixes #10307
This commit is contained in:
@@ -123,6 +123,10 @@ func (e *errorResource) Exif() *exif.ExifInfo {
|
||||
panic(e.ResourceError)
|
||||
}
|
||||
|
||||
func (e *errorResource) Colors() ([]string, error) {
|
||||
panic(e.ResourceError)
|
||||
}
|
||||
|
||||
func (e *errorResource) DecodeImage() (image.Image, error) {
|
||||
panic(e.ResourceError)
|
||||
}
|
||||
|
@@ -30,6 +30,8 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
color_extractor "github.com/marekm4/color-extractor"
|
||||
|
||||
"github.com/gohugoio/hugo/common/paths"
|
||||
|
||||
"github.com/disintegration/gift"
|
||||
@@ -64,6 +66,9 @@ type imageResource struct {
|
||||
metaInitErr error
|
||||
meta *imageMeta
|
||||
|
||||
dominantColorInit sync.Once
|
||||
dominantColors []string
|
||||
|
||||
baseResource
|
||||
}
|
||||
|
||||
@@ -135,6 +140,24 @@ func (i *imageResource) getExif() *exif.ExifInfo {
|
||||
return i.meta.Exif
|
||||
}
|
||||
|
||||
// Colors returns a slice of the most dominant colors in an image
|
||||
// using a simple histogram method.
|
||||
func (i *imageResource) Colors() ([]string, error) {
|
||||
var err error
|
||||
i.dominantColorInit.Do(func() {
|
||||
var img image.Image
|
||||
img, err = i.DecodeImage()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
colors := color_extractor.ExtractColors(img)
|
||||
for _, c := range colors {
|
||||
i.dominantColors = append(i.dominantColors, images.ColorToHexString(c))
|
||||
}
|
||||
})
|
||||
return i.dominantColors, nil
|
||||
}
|
||||
|
||||
// Clone is for internal use.
|
||||
func (i *imageResource) Clone() resource.Resource {
|
||||
gr := i.baseResource.Clone().(baseResource)
|
||||
|
@@ -84,6 +84,10 @@ func TestImageTransformBasic(t *testing.T) {
|
||||
c.Assert(img.Height(), qt.Equals, h)
|
||||
}
|
||||
|
||||
colors, err := image.Colors()
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(colors, qt.DeepEquals, []string{"#2d2f33", "#a49e93", "#d39e59", "#a76936", "#737a84", "#7c838b"})
|
||||
|
||||
c.Assert(image.RelPermalink(), qt.Equals, "/a/sunset.jpg")
|
||||
c.Assert(image.ResourceType(), qt.Equals, "image")
|
||||
assertWidthHeight(image, 900, 562)
|
||||
|
@@ -45,6 +45,14 @@ func ReplaceColorInPalette(c color.Color, p color.Palette) {
|
||||
p[p.Index(c)] = c
|
||||
}
|
||||
|
||||
// ColorToHexString converts a color to a hex string.
|
||||
func ColorToHexString(c color.Color) string {
|
||||
r, g, b, a := c.RGBA()
|
||||
rgba := color.RGBA{uint8(r), uint8(g), uint8(b), uint8(a)}
|
||||
return fmt.Sprintf("#%.2x%.2x%.2x", rgba.R, rgba.G, rgba.B)
|
||||
|
||||
}
|
||||
|
||||
func hexStringToColor(s string) (color.Color, error) {
|
||||
s = strings.TrimPrefix(s, "#")
|
||||
|
||||
|
@@ -60,6 +60,30 @@ func TestHexStringToColor(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestColorToHexString(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
for _, test := range []struct {
|
||||
arg color.Color
|
||||
expect string
|
||||
}{
|
||||
{color.White, "#ffffff"},
|
||||
{color.Black, "#000000"},
|
||||
{color.RGBA{R: 0x42, G: 0x87, B: 0xf5, A: 0xff}, "#4287f5"},
|
||||
} {
|
||||
|
||||
test := test
|
||||
c.Run(test.expect, func(c *qt.C) {
|
||||
c.Parallel()
|
||||
|
||||
result := ColorToHexString(test.arg)
|
||||
|
||||
c.Assert(result, qt.Equals, test.expect)
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddColorToPalette(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
|
@@ -48,6 +48,10 @@ type ImageResourceOps interface {
|
||||
// Exif returns an ExifInfo object containing Image metadata.
|
||||
Exif() *exif.ExifInfo
|
||||
|
||||
// Colors returns a slice of the most dominant colors in an image
|
||||
// using a simple histogram method.
|
||||
Colors() ([]string, error)
|
||||
|
||||
// Internal
|
||||
DecodeImage() (image.Image, error)
|
||||
}
|
||||
|
@@ -213,6 +213,10 @@ func (r *resourceAdapter) Exif() *exif.ExifInfo {
|
||||
return r.getImageOps().Exif()
|
||||
}
|
||||
|
||||
func (r *resourceAdapter) Colors() ([]string, error) {
|
||||
return r.getImageOps().Colors()
|
||||
}
|
||||
|
||||
func (r *resourceAdapter) Key() string {
|
||||
r.init(false, false)
|
||||
return r.target.(resource.Identifier).Key()
|
||||
|
Reference in New Issue
Block a user