summaryrefslogtreecommitdiffstats
path: root/resources
diff options
context:
space:
mode:
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>2022-09-21 16:24:54 +0200
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>2022-09-22 15:57:35 +0200
commita4028112e340b20909746daca0f8cabb09cbf28f (patch)
tree3952d26c1d6fd4ac543617272b7a0311cd119339 /resources
parent08f0984f918bb97d35c1765d3b94b1a9a1a3b9cd (diff)
resources/images: Add $image.Colors
Which returns the most dominant colors of an image using a simple histogram method. Fixes #10307
Diffstat (limited to 'resources')
-rw-r--r--resources/errorResource.go4
-rw-r--r--resources/image.go23
-rw-r--r--resources/image_test.go4
-rw-r--r--resources/images/color.go8
-rw-r--r--resources/images/color_test.go24
-rw-r--r--resources/images/image_resource.go4
-rw-r--r--resources/transform.go4
7 files changed, 71 insertions, 0 deletions
diff --git a/resources/errorResource.go b/resources/errorResource.go
index 81375cc48..e9411c3db 100644
--- a/resources/errorResource.go
+++ b/resources/errorResource.go
@@ -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)
}
diff --git a/resources/image.go b/resources/image.go
index 8551cc2ab..ea8a21156 100644
--- a/resources/image.go
+++ b/resources/image.go
@@ -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)
diff --git a/resources/image_test.go b/resources/image_test.go
index 153a4e8c4..111ce66db 100644
--- a/resources/image_test.go
+++ b/resources/image_test.go
@@ -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)
diff --git a/resources/images/color.go b/resources/images/color.go
index 057a9fb71..0eedecb89 100644
--- a/resources/images/color.go
+++ b/resources/images/color.go
@@ -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, "#")
diff --git a/resources/images/color_test.go b/resources/images/color_test.go
index 52871e691..c3860a82c 100644
--- a/resources/images/color_test.go
+++ b/resources/images/color_test.go
@@ -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)
diff --git a/resources/images/image_resource.go b/resources/images/image_resource.go
index e0fec15a0..4e66b010c 100644
--- a/resources/images/image_resource.go
+++ b/resources/images/image_resource.go
@@ -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)
}
diff --git a/resources/transform.go b/resources/transform.go
index 7d81f9b21..14120fda0 100644
--- a/resources/transform.go
+++ b/resources/transform.go
@@ -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()