]> git.maquefel.me Git - brevno-suite/hugo/commitdiff
Add images.Opacity filter
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Thu, 21 Sep 2023 20:32:32 +0000 (22:32 +0200)
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Fri, 22 Sep 2023 13:03:16 +0000 (15:03 +0200)
Fixes #11471

docs/content/en/functions/images/index.md
resources/image_test.go
resources/images/filters.go
resources/images/opacity.go [new file with mode: 0644]
resources/testdata/golden/gopher-hero8_huaa0cd7d2cfc14ff32a57f171896f2285_13327_filter_10590793696706257122.png [new file with mode: 0644]
resources/testdata/golden/gradient-circle_huf3d35257a40a8d6f525263a856c5ecfd_20069_filter_10590793696706257122.png [new file with mode: 0644]
resources/testdata/golden/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_filter_16531506165985954191.jpg [new file with mode: 0644]
resources/testdata/golden/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_filter_18171945436439920693.jpg [new file with mode: 0644]

index e42f4cdd8ab3afd0c20404235a55fa486aa0509e..fcd0796e3fe78d5966b6f96c36bd647459613cac 100644 (file)
@@ -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.
index de32f06282ceadb830edd254b54829aa6b90e8a3..751ef3f5d18c45230958afabb8fa4c15967fa430 100644 (file)
@@ -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)
index 90667af7c90875821c545c994aaa5b87afae734b..63e90d2ada954c56c11a399d8f7c23f2a34d16fe 100644 (file)
@@ -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 (file)
index 0000000..4b60e30
--- /dev/null
@@ -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 (file)
index 0000000..935c239
Binary files /dev/null and b/resources/testdata/golden/gopher-hero8_huaa0cd7d2cfc14ff32a57f171896f2285_13327_filter_10590793696706257122.png 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 (file)
index 0000000..fe39286
Binary files /dev/null and b/resources/testdata/golden/gradient-circle_huf3d35257a40a8d6f525263a856c5ecfd_20069_filter_10590793696706257122.png 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 (file)
index 0000000..bc43f4e
Binary files /dev/null and b/resources/testdata/golden/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_filter_16531506165985954191.jpg 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 (file)
index 0000000..80f06bf
Binary files /dev/null and b/resources/testdata/golden/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_filter_18171945436439920693.jpg differ