mirror of
				https://github.com/gohugoio/hugo.git
				synced 2024-05-11 05:54:58 +00:00 
			
		
		
		
	images: Add images.Overlay filter
This allows for constructs ala:
```
{{ $overlay := $img.Filter (images.Overlay $logo 50 50 )}}
```
Or:
```
{{ $logoFilter := (images.Overlay $logo 50 50 ) }}
{{ $overlay := $img | images.Filter $logoFilter }}
```
Which will overlay the logo in the top left corner (x=50, y=50) of `$img`.
Fixes #8057
Fixes #4595
Updates #6731
			
			
This commit is contained in:
		@@ -17,6 +17,30 @@ toc: true
 | 
			
		||||
 | 
			
		||||
See [images.Filter](#filter) for how to apply these filters to an image.
 | 
			
		||||
 | 
			
		||||
### Overlay
 | 
			
		||||
 | 
			
		||||
{{< new-in "0.80.0" >}}
 | 
			
		||||
 | 
			
		||||
{{% funcsig %}}
 | 
			
		||||
images.Overlay SRC X Y
 | 
			
		||||
{{% /funcsig %}}
 | 
			
		||||
 | 
			
		||||
Overlay creates a filter that overlays the source image at position x y, e.g:
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
```go-html-template
 | 
			
		||||
{{ $logoFilter := (images.Overlay $logo 50 50 ) }}
 | 
			
		||||
{{ $img := $img | images.Filter $logoFilter }}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
A shorter version of the above, if you only need to apply the filter once:
 | 
			
		||||
 | 
			
		||||
```go-html-template
 | 
			
		||||
{{ $img := $img.Filter (images.Overlay $logo 50 50 )}}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The above will overlay `$logo` in the upper left corner of `$img` (at position `x=50, y=50`).
 | 
			
		||||
 | 
			
		||||
### Brightness
 | 
			
		||||
 | 
			
		||||
{{% funcsig %}}
 | 
			
		||||
 
 | 
			
		||||
@@ -242,7 +242,7 @@ func (i *imageResource) doWithImageConfig(conf images.ImageConfig, f func(src im
 | 
			
		||||
		errOp := conf.Action
 | 
			
		||||
		errPath := i.getSourceFilename()
 | 
			
		||||
 | 
			
		||||
		src, err := i.decodeSource()
 | 
			
		||||
		src, err := i.DecodeImage()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, nil, &os.PathError{Op: errOp, Path: errPath, Err: err}
 | 
			
		||||
		}
 | 
			
		||||
@@ -324,7 +324,9 @@ func (i *imageResource) decodeImageConfig(action, spec string) (images.ImageConf
 | 
			
		||||
	return conf, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (i *imageResource) decodeSource() (image.Image, error) {
 | 
			
		||||
// DecodeImage decodes the image source into an Image.
 | 
			
		||||
// This an internal method and may change.
 | 
			
		||||
func (i *imageResource) DecodeImage() (image.Image, error) {
 | 
			
		||||
	f, err := i.ReadSeekCloser()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, _errors.Wrap(err, "failed to open image for decode")
 | 
			
		||||
 
 | 
			
		||||
@@ -533,6 +533,11 @@ func TestImageOperationsGolden(t *testing.T) {
 | 
			
		||||
		fmt.Println(workDir)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	gopher := fetchImageForSpec(spec, c, "gopher-hero8.png")
 | 
			
		||||
	var err error
 | 
			
		||||
	gopher, err = gopher.Resize("30x")
 | 
			
		||||
	c.Assert(err, qt.IsNil)
 | 
			
		||||
 | 
			
		||||
	// Test PNGs with alpha channel.
 | 
			
		||||
	for _, img := range []string{"gopher-hero8.png", "gradient-circle.png"} {
 | 
			
		||||
		orig := fetchImageForSpec(spec, c, img)
 | 
			
		||||
@@ -589,6 +594,7 @@ func TestImageOperationsGolden(t *testing.T) {
 | 
			
		||||
			f.Invert(),
 | 
			
		||||
			f.Hue(22),
 | 
			
		||||
			f.Contrast(32.5),
 | 
			
		||||
			f.Overlay(gopher.(images.ImageSource), 20, 30),
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		resized, err := orig.Fill("400x200 center")
 | 
			
		||||
 
 | 
			
		||||
@@ -25,6 +25,14 @@ const filterAPIVersion = 0
 | 
			
		||||
type Filters struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Overlay creates a filter that overlays src at position x y.
 | 
			
		||||
func (*Filters) Overlay(src ImageSource, x, y interface{}) gift.Filter {
 | 
			
		||||
	return filter{
 | 
			
		||||
		Options: newFilterOpts(src.Key(), x, y),
 | 
			
		||||
		Filter:  overlayFilter{src: src, x: cast.ToInt(x), y: cast.ToInt(y)},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Brightness creates a filter that changes the brightness of an image.
 | 
			
		||||
// The percentage parameter must be in range (-100, 100).
 | 
			
		||||
func (*Filters) Brightness(percentage interface{}) gift.Filter {
 | 
			
		||||
 
 | 
			
		||||
@@ -325,3 +325,9 @@ func IsOpaque(img image.Image) bool {
 | 
			
		||||
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ImageSource identifies and decodes an image.
 | 
			
		||||
type ImageSource interface {
 | 
			
		||||
	DecodeImage() (image.Image, error)
 | 
			
		||||
	Key() string
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										43
									
								
								resources/images/overlay.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								resources/images/overlay.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,43 @@
 | 
			
		||||
// Copyright 2020 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 (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"image"
 | 
			
		||||
	"image/draw"
 | 
			
		||||
 | 
			
		||||
	"github.com/disintegration/gift"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var _ gift.Filter = (*overlayFilter)(nil)
 | 
			
		||||
 | 
			
		||||
type overlayFilter struct {
 | 
			
		||||
	src  ImageSource
 | 
			
		||||
	x, y int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f overlayFilter) Draw(dst draw.Image, src image.Image, options *gift.Options) {
 | 
			
		||||
	overlaySrc, err := f.src.DecodeImage()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		panic(fmt.Sprintf("failed to decode image: %s", err))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	gift.New().Draw(dst, src)
 | 
			
		||||
	gift.New().DrawAt(dst, overlaySrc, image.Pt(f.x, f.y), gift.OverOperator)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f overlayFilter) Bounds(srcBounds image.Rectangle) image.Rectangle {
 | 
			
		||||
	return image.Rect(0, 0, srcBounds.Dx(), srcBounds.Dy())
 | 
			
		||||
}
 | 
			
		||||
@@ -14,6 +14,8 @@
 | 
			
		||||
package resource
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"image"
 | 
			
		||||
 | 
			
		||||
	"github.com/gohugoio/hugo/common/maps"
 | 
			
		||||
	"github.com/gohugoio/hugo/langs"
 | 
			
		||||
	"github.com/gohugoio/hugo/media"
 | 
			
		||||
@@ -59,6 +61,9 @@ type ImageOps interface {
 | 
			
		||||
	Resize(spec string) (Image, error)
 | 
			
		||||
	Filter(filters ...interface{}) (Image, error)
 | 
			
		||||
	Exif() *exif.Exif
 | 
			
		||||
 | 
			
		||||
	// Internal
 | 
			
		||||
	DecodeImage() (image.Image, error)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type ResourceTypeProvider interface {
 | 
			
		||||
 
 | 
			
		||||
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 60 KiB  | 
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 26 KiB  | 
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 1.3 KiB  | 
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 6.7 KiB  | 
@@ -16,6 +16,7 @@ package resources
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"image"
 | 
			
		||||
	"io"
 | 
			
		||||
	"path"
 | 
			
		||||
	"strings"
 | 
			
		||||
@@ -264,6 +265,10 @@ func (r *resourceAdapter) Width() int {
 | 
			
		||||
	return r.getImageOps().Width()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *resourceAdapter) DecodeImage() (image.Image, error) {
 | 
			
		||||
	return r.getImageOps().DecodeImage()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *resourceAdapter) getImageOps() resource.ImageOps {
 | 
			
		||||
	img, ok := r.target.(resource.ImageOps)
 | 
			
		||||
	if !ok {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user