summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>2023-09-21 22:32:32 +0200
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>2023-09-22 15:03:16 +0200
commitf9b3c0f4860d2664d2abf0513ee8055a37a7fdd5 (patch)
tree00e25ce46931bd829aecaaa8bc5c97091a4a85ed
parent11fcda971c716cb63764c4e54026d0916311fe23 (diff)
Add images.Opacity filter
Fixes #11471
-rw-r--r--docs/content/en/functions/images/index.md13
-rw-r--r--resources/image_test.go26
-rw-r--r--resources/images/filters.go12
-rw-r--r--resources/images/opacity.go39
-rw-r--r--resources/testdata/golden/gopher-hero8_huaa0cd7d2cfc14ff32a57f171896f2285_13327_filter_10590793696706257122.pngbin0 -> 59248 bytes
-rw-r--r--resources/testdata/golden/gradient-circle_huf3d35257a40a8d6f525263a856c5ecfd_20069_filter_10590793696706257122.pngbin0 -> 41317 bytes
-rw-r--r--resources/testdata/golden/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_filter_16531506165985954191.jpgbin0 -> 57739 bytes
-rw-r--r--resources/testdata/golden/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_filter_18171945436439920693.jpgbin0 -> 45713 bytes
8 files changed, 76 insertions, 14 deletions
diff --git a/docs/content/en/functions/images/index.md b/docs/content/en/functions/images/index.md
index e42f4cdd8..fcd0796e3 100644
--- a/docs/content/en/functions/images/index.md
+++ b/docs/content/en/functions/images/index.md
@@ -34,6 +34,19 @@ A shorter version of the above, if you only need to apply the filter once:
The above will overlay `$logo` in the upper left corner of `$img` (at position `x=50, y=50`).
+## Opacity
+
+{{% funcsig %}}
+images.Opacity SRC OPACITY
+{{% /funcsig %}}
+
+Opacity creates a filter that changes the opacity of an image.
+The OPACITY parameter must be in range (0, 1).
+
+```go-html-template
+{{ $img := $img.Filter (images.Opacity 0.5 )}}
+```
+
## Text
Using the `Text` filter, you can add text to an image.
diff --git a/resources/image_test.go b/resources/image_test.go
index de32f0628..751ef3f5d 100644
--- a/resources/image_test.go
+++ b/resources/image_test.go
@@ -63,7 +63,7 @@ var eq = qt.CmpEquals(
}
return p1.Name() == p2.Name() && p1.Size() == p2.Size() && p1.IsDir() == p2.IsDir()
}),
- //cmp.Comparer(func(p1, p2 *genericResource) bool { return p1 == p2 }),
+ // cmp.Comparer(func(p1, p2 *genericResource) bool { return p1 == p2 }),
cmp.Comparer(func(m1, m2 media.Type) bool {
return m1.Type == m2.Type
}),
@@ -162,7 +162,6 @@ func TestImageTransformBasic(t *testing.T) {
croppedAgain, err := image.Crop("300x300 topRight")
c.Assert(err, qt.IsNil)
c.Assert(cropped, qt.Equals, croppedAgain)
-
}
func TestImageTransformFormat(t *testing.T) {
@@ -267,7 +266,6 @@ func TestImageBugs(t *testing.T) {
c.Assert(resized, qt.Not(qt.IsNil))
c.Assert(resized.Width(), qt.Equals, 100)
c.Assert(resized.RelPermalink(), qt.Equals, "/a/_hu59e56ffff1bc1d8d122b1403d34e039f_90587_c876768085288f41211f768147ba2647.jpg")
-
})
// Issue #6137
@@ -278,7 +276,6 @@ func TestImageBugs(t *testing.T) {
c.Assert(err, qt.IsNil)
c.Assert(resized, qt.Not(qt.IsNil))
c.Assert(resized.Width(), qt.Equals, 200)
-
})
// Issue #7955
@@ -307,9 +304,7 @@ func TestImageBugs(t *testing.T) {
c.Assert(resized.Width(), qt.Equals, test.targetWH)
c.Assert(resized.Height(), qt.Equals, test.targetWH)
})
-
}
-
})
}
@@ -613,7 +608,6 @@ func TestImageOperationsGoldenWebp(t *testing.T) {
dir2 := filepath.FromSlash("testdata/golden_webp")
assetGoldenDirs(c, dir1, dir2)
-
}
func TestImageOperationsGolden(t *testing.T) {
@@ -621,7 +615,7 @@ func TestImageOperationsGolden(t *testing.T) {
c.Parallel()
// Note, if you're enabling this on a MacOS M1 (ARM) you need to run the test with GOARCH=amd64.
- // GOARCH=amd64 go test -timeout 30s -run "^TestImageOperationsGolden$" ./resources -v
+ // GOARCH=amd64 go test -count 1 -timeout 30s -run "^TestImageOperationsGolden$" ./resources -v
// The above will print out a folder.
// Replace testdata/golden with resources/_gen/images in that folder.
devMode := false
@@ -644,6 +638,10 @@ func TestImageOperationsGolden(t *testing.T) {
gopher, err = gopher.Resize("30x")
c.Assert(err, qt.IsNil)
+ f := &images.Filters{}
+
+ sunset := fetchImageForSpec(spec, c, "sunset.jpg")
+
// Test PNGs with alpha channel.
for _, img := range []string{"gopher-hero8.png", "gradient-circle.png"} {
orig := fetchImageForSpec(spec, c, img)
@@ -653,7 +651,15 @@ func TestImageOperationsGolden(t *testing.T) {
rel := resized.RelPermalink()
c.Assert(rel, qt.Not(qt.Equals), "")
+
}
+
+ // Check the Opacity filter.
+ opacity30, err := orig.Filter(f.Opacity(30))
+ c.Assert(err, qt.IsNil)
+ overlay, err := sunset.Filter(f.Overlay(opacity30.(images.ImageSource), 20, 20))
+ rel := overlay.RelPermalink()
+ c.Assert(rel, qt.Not(qt.Equals), "")
}
// A simple Gif file (no animation).
@@ -699,8 +705,6 @@ func TestImageOperationsGolden(t *testing.T) {
c.Assert(rel, qt.Not(qt.Equals), "")
}
- f := &images.Filters{}
-
filters := []gift.Filter{
f.Grayscale(),
f.GaussianBlur(6),
@@ -746,11 +750,9 @@ func TestImageOperationsGolden(t *testing.T) {
dir2 := filepath.FromSlash("testdata/golden")
assetGoldenDirs(c, dir1, dir2)
-
}
func assetGoldenDirs(c *qt.C, dir1, dir2 string) {
-
// The two dirs above should now be the same.
dirinfos1, err := os.ReadDir(dir1)
c.Assert(err, qt.IsNil)
diff --git a/resources/images/filters.go b/resources/images/filters.go
index 90667af7c..63e90d2ad 100644
--- a/resources/images/filters.go
+++ b/resources/images/filters.go
@@ -28,8 +28,7 @@ import (
// Increment for re-generation of images using these filters.
const filterAPIVersion = 0
-type Filters struct {
-}
+type Filters struct{}
// Overlay creates a filter that overlays src at position x y.
func (*Filters) Overlay(src ImageSource, x, y any) gift.Filter {
@@ -39,6 +38,15 @@ func (*Filters) Overlay(src ImageSource, x, y any) gift.Filter {
}
}
+// Opacity creates a filter that changes the opacity of an image.
+// The opacity parameter must be in range (0, 1).
+func (*Filters) Opacity(opacity any) gift.Filter {
+ return filter{
+ Options: newFilterOpts(opacity),
+ Filter: opacityFilter{opacity: cast.ToFloat32(opacity)},
+ }
+}
+
// Text creates a filter that draws text with the given options.
func (*Filters) Text(text string, options ...any) gift.Filter {
tf := textFilter{
diff --git a/resources/images/opacity.go b/resources/images/opacity.go
new file mode 100644
index 000000000..4b60e30a4
--- /dev/null
+++ b/resources/images/opacity.go
@@ -0,0 +1,39 @@
+// Copyright 2023 The Hugo Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package images
+
+import (
+ "image"
+ "image/color"
+ "image/draw"
+
+ "github.com/disintegration/gift"
+)
+
+var _ gift.Filter = (*opacityFilter)(nil)
+
+type opacityFilter struct {
+ opacity float32
+}
+
+func (f opacityFilter) Draw(dst draw.Image, src image.Image, options *gift.Options) {
+ // 0 is fully transparent and 255 is opaque.
+ alpha := uint8(f.opacity * 255)
+ mask := image.NewUniform(color.Alpha{alpha})
+ draw.DrawMask(dst, dst.Bounds(), src, image.Point{}, mask, image.Point{}, draw.Over)
+}
+
+func (f opacityFilter) Bounds(srcBounds image.Rectangle) image.Rectangle {
+ return image.Rect(0, 0, srcBounds.Dx(), srcBounds.Dy())
+}
diff --git a/resources/testdata/golden/gopher-hero8_huaa0cd7d2cfc14ff32a57f171896f2285_13327_filter_10590793696706257122.png b/resources/testdata/golden/gopher-hero8_huaa0cd7d2cfc14ff32a57f171896f2285_13327_filter_10590793696706257122.png
new file mode 100644
index 000000000..935c2391b
--- /dev/null
+++ b/resources/testdata/golden/gopher-hero8_huaa0cd7d2cfc14ff32a57f171896f2285_13327_filter_10590793696706257122.png
Binary files differ
diff --git a/resources/testdata/golden/gradient-circle_huf3d35257a40a8d6f525263a856c5ecfd_20069_filter_10590793696706257122.png b/resources/testdata/golden/gradient-circle_huf3d35257a40a8d6f525263a856c5ecfd_20069_filter_10590793696706257122.png
new file mode 100644
index 000000000..fe39286bd
--- /dev/null
+++ b/resources/testdata/golden/gradient-circle_huf3d35257a40a8d6f525263a856c5ecfd_20069_filter_10590793696706257122.png
Binary files differ
diff --git a/resources/testdata/golden/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_filter_16531506165985954191.jpg b/resources/testdata/golden/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_filter_16531506165985954191.jpg
new file mode 100644
index 000000000..bc43f4ef9
--- /dev/null
+++ b/resources/testdata/golden/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_filter_16531506165985954191.jpg
Binary files differ
diff --git a/resources/testdata/golden/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_filter_18171945436439920693.jpg b/resources/testdata/golden/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_filter_18171945436439920693.jpg
new file mode 100644
index 000000000..80f06bf66
--- /dev/null
+++ b/resources/testdata/golden/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_filter_18171945436439920693.jpg
Binary files differ