Skip to content

Commit

Permalink
Add webp image encoding support
Browse files Browse the repository at this point in the history
  • Loading branch information
bep committed Apr 12, 2021
1 parent 9b83f45 commit 30846fc
Show file tree
Hide file tree
Showing 13 changed files with 174 additions and 13 deletions.
6 changes: 5 additions & 1 deletion common/hugo/hugo.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,11 @@ func GetDependencyList() []string {
}

if IsExtended {
deps = append(deps, formatDep("github.com/sass/libsass", "3.6.4"))
deps = append(
deps,
formatDep("github.com/sass/libsass", "3.6.4"),
formatDep("github.com/webmproject/libwebp", "v1.2.0"),
)
}

bi, ok := debug.ReadBuildInfo()
Expand Down
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ require (
github.com/bep/gitmap v1.1.2
github.com/bep/godartsass v0.12.0
github.com/bep/golibsass v0.7.0
github.com/bep/gowebp v0.1.0
github.com/bep/tmc v0.5.1
github.com/cli/safeexec v1.0.0
github.com/disintegration/gift v1.2.1
Expand Down Expand Up @@ -59,7 +60,7 @@ require (
github.com/yuin/goldmark v1.3.2
github.com/yuin/goldmark-highlighting v0.0.0-20200307114337-60d527fdb691
gocloud.dev v0.20.0
golang.org/x/image v0.0.0-20191214001246-9130b4cfad52
golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb
golang.org/x/net v0.0.0-20201224014010-6772e930b67b
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a
golang.org/x/text v0.3.4
Expand Down
16 changes: 16 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,20 @@ github.com/bep/godartsass v0.12.0 h1:VvGLA4XpXUjKvp53SI05YFLhRFJ78G+Ybnlaz6Oul7E
github.com/bep/godartsass v0.12.0/go.mod h1:nXQlHHk4H1ghUk6n/JkYKG5RD43yJfcfp5aHRqT/pc4=
github.com/bep/golibsass v0.7.0 h1:/ocxgtPZ5rgp7FA+mktzyent+fAg82tJq4iMsTMBAtA=
github.com/bep/golibsass v0.7.0/go.mod h1:DL87K8Un/+pWUS75ggYv41bliGiolxzDKWJAq3eJ1MA=
github.com/bep/gowebp v0.0.0-20210408171434-03ecbe0b5d53 h1:bTIhFx2ZEAZD74LwuVdrdZ4070bE9UE5oR5NTBYLtVs=
github.com/bep/gowebp v0.0.0-20210408171434-03ecbe0b5d53/go.mod h1:ZhFodwdiFp8ehGJpF4LdPl6unxZm9lLFjxD3z2h2AgI=
github.com/bep/gowebp v0.0.0-20210409123354-5e38121e4f6b h1:LLrQFlG0VSxmyz3izTUQnPOGf7Mjiy7wlEu2sDLA+qg=
github.com/bep/gowebp v0.0.0-20210409123354-5e38121e4f6b/go.mod h1:ZhFodwdiFp8ehGJpF4LdPl6unxZm9lLFjxD3z2h2AgI=
github.com/bep/gowebp v0.0.0-20210410152255-50a32861b5a2 h1:uEpPD0fLZs5IjgF/96LqWHUNY9Pr/0KqLWIQ4gJnYhY=
github.com/bep/gowebp v0.0.0-20210410152255-50a32861b5a2/go.mod h1:ZhFodwdiFp8ehGJpF4LdPl6unxZm9lLFjxD3z2h2AgI=
github.com/bep/gowebp v0.0.0-20210410161412-b86a3337b39f h1:hvhG2nwoIvHhFnL8GnYtOquHE6dG+mHwthugLqf4spY=
github.com/bep/gowebp v0.0.0-20210410161412-b86a3337b39f/go.mod h1:ZhFodwdiFp8ehGJpF4LdPl6unxZm9lLFjxD3z2h2AgI=
github.com/bep/gowebp v0.0.0-20210411110227-3a211f6b6461 h1:5HLIo8LF4iKFdxPBDo9CO8oTac18mAx7FJsQG6MNbCU=
github.com/bep/gowebp v0.0.0-20210411110227-3a211f6b6461/go.mod h1:ZhFodwdiFp8ehGJpF4LdPl6unxZm9lLFjxD3z2h2AgI=
github.com/bep/gowebp v0.0.0-20210411155607-38d8f20d562b h1:VIW6UmIG4ogbswbDFBjVm6/7j9I5i0GouDJ2USn/NUI=
github.com/bep/gowebp v0.0.0-20210411155607-38d8f20d562b/go.mod h1:ZhFodwdiFp8ehGJpF4LdPl6unxZm9lLFjxD3z2h2AgI=
github.com/bep/gowebp v0.1.0 h1:4/iQpfnxHyXs3x/aTxMMdOpLEQQhFmF6G7EieWPTQyo=
github.com/bep/gowebp v0.1.0/go.mod h1:ZhFodwdiFp8ehGJpF4LdPl6unxZm9lLFjxD3z2h2AgI=
github.com/bep/tmc v0.5.1 h1:CsQnSC6MsomH64gw0cT5f+EwQDcvZz4AazKunFwTpuI=
github.com/bep/tmc v0.5.1/go.mod h1:tGYHN8fS85aJPhDLgXETVKp+PR382OvFi2+q2GkGsq0=
github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY=
Expand Down Expand Up @@ -712,6 +726,8 @@ golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMx
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20191214001246-9130b4cfad52 h1:2fktqPPvDiVEEVT/vSTeoUPXfmRxRaGy6GU8jypvEn0=
golang.org/x/image v0.0.0-20191214001246-9130b4cfad52/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb h1:fqpd0EBDzlHRCjiphRR5Zo/RSWWQlWv34418dnEixWk=
golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
Expand Down
2 changes: 2 additions & 0 deletions media/mediaType.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ var (
GIFType = newMediaType("image", "gif", []string{"gif"})
TIFFType = newMediaType("image", "tiff", []string{"tif", "tiff"})
BMPType = newMediaType("image", "bmp", []string{"bmp"})
WEBPType = newMediaType("image", "webp", []string{"webp"})

// Common video types
AVIType = newMediaType("video", "x-msvideo", []string{"avi"})
Expand Down Expand Up @@ -214,6 +215,7 @@ var DefaultTypes = Types{
TOMLType,
PNGType,
JPEGType,
WEBPType,
AVIType,
MPEGType,
MP4Type,
Expand Down
2 changes: 1 addition & 1 deletion media/mediaType_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ func TestDefaultTypes(t *testing.T) {

}

c.Assert(len(DefaultTypes), qt.Equals, 26)
c.Assert(len(DefaultTypes), qt.Equals, 27)
}

func TestGetByType(t *testing.T) {
Expand Down
2 changes: 1 addition & 1 deletion resources/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ func (i *imageResource) Fill(spec string) (resource.Image, error) {
}

func (i *imageResource) Filter(filters ...interface{}) (resource.Image, error) {
conf := i.Proc.GetDefaultImageConfig("filter")
conf := images.GetDefaultImageConfig("filter")

var gfilters []gift.Filter

Expand Down
41 changes: 41 additions & 0 deletions resources/image_extended_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright 2019 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.

// +build extended

package resources

import (
"testing"

"github.com/gohugoio/hugo/media"

qt "github.com/frankban/quicktest"
)

func TestImageResizeWebP(t *testing.T) {
c := qt.New(t)

image := fetchImage(c, "sunset.webp")

c.Assert(image.MediaType(), qt.Equals, media.WEBPType)
c.Assert(image.RelPermalink(), qt.Equals, "/a/sunset.webp")
c.Assert(image.ResourceType(), qt.Equals, "image")
c.Assert(image.Exif(), qt.IsNil)

resized, err := image.Resize("123x")
c.Assert(err, qt.IsNil)
c.Assert(image.MediaType(), qt.Equals, media.WEBPType)
c.Assert(resized.RelPermalink(), qt.Equals, "/a/sunset_hu36ee0b61ba924719ad36da960c273f96_59826_123x0_resize_q68_h2_linear.webp")
c.Assert(resized.Width(), qt.Equals, 123)
}
26 changes: 24 additions & 2 deletions resources/images/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import (
"strconv"
"strings"

"github.com/bep/gowebp/libwebp/webpoptions"

"github.com/disintegration/gift"

"github.com/mitchellh/mapstructure"
Expand All @@ -40,6 +42,7 @@ var (
".tiff": TIFF,
".bmp": BMP,
".gif": GIF,
".webp": WEBP,
}

// Add or increment if changes to an image format's processing requires
Expand All @@ -65,6 +68,15 @@ var anchorPositions = map[string]gift.Anchor{
strings.ToLower("BottomRight"): gift.BottomRightAnchor,
}

// These encoding hints are currently only relevant for Webp.
var hints = map[string]webpoptions.EncodingPreset{
"picture": webpoptions.EncodingPresetPicture,
"photo": webpoptions.EncodingPresetPhoto,
"drawing": webpoptions.EncodingPresetDrawing,
"icon": webpoptions.EncodingPresetIcon,
"text": webpoptions.EncodingPresetText,
}

var imageFilters = map[string]gift.Resampling{

strings.ToLower("NearestNeighbor"): gift.NearestNeighborResampling,
Expand Down Expand Up @@ -145,7 +157,7 @@ func DecodeConfig(m map[string]interface{}) (ImagingConfig, error) {

func DecodeImageConfig(action, config string, defaults Imaging) (ImageConfig, error) {
var (
c ImageConfig
c ImageConfig = GetDefaultImageConfig(action)
err error
)

Expand All @@ -167,6 +179,8 @@ func DecodeImageConfig(action, config string, defaults Imaging) (ImageConfig, er
} else if filter, ok := imageFilters[part]; ok {
c.Filter = filter
c.FilterStr = part
} else if hint, ok := hints[part]; ok {
c.Hint = hint
} else if part[0] == '#' {
c.BgColorStr = part[1:]
c.BgColor, err = hexStringToColor(c.BgColorStr)
Expand Down Expand Up @@ -244,7 +258,7 @@ type ImageConfig struct {
Key string

// Quality ranges from 1 to 100 inclusive, higher is better.
// This is only relevant for JPEG images.
// This is only relevant for JPEG and WEBP images.
// Default is 75.
Quality int

Expand All @@ -260,6 +274,10 @@ type ImageConfig struct {
BgColor color.Color
BgColorStr string

// Hint about what type of picture this is. Used to optimize encoding
// when target is set to webp.
Hint webpoptions.EncodingPreset

Width int
Height int

Expand Down Expand Up @@ -289,6 +307,10 @@ func (i ImageConfig) GetKey(format Format) string {
k += "_bg" + i.BgColorStr
}

if i.TargetFormat == WEBP {
k += "_h" + strconv.Itoa(int(i.Hint))
}

anchor := i.AnchorStr
if anchor == smartCropIdentifier {
anchor = anchor + strconv.Itoa(smartCropVersionNumber)
Expand Down
3 changes: 1 addition & 2 deletions resources/images/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,7 @@ func TestDecodeImageConfig(t *testing.T) {
}

func newImageConfig(width, height, quality, rotate int, filter, anchor, bgColor string) ImageConfig {
var c ImageConfig
c.Action = "resize"
var c ImageConfig = GetDefaultImageConfig("resize")
c.Width = width
c.Height = height
c.Quality = quality
Expand Down
26 changes: 21 additions & 5 deletions resources/images/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ import (
"io"
"sync"

"github.com/bep/gowebp/libwebp/webpoptions"
"github.com/gohugoio/hugo/resources/images/webp"

"github.com/gohugoio/hugo/media"
"github.com/gohugoio/hugo/resources/images/exif"

Expand Down Expand Up @@ -89,6 +92,15 @@ func (i *Image) EncodeTo(conf ImageConfig, img image.Image, w io.Writer) error {

case BMP:
return bmp.Encode(w, img)
case WEBP:
return webp.Encode(
w,
img, webpoptions.EncodingOptions{
Quality: conf.Quality,
EncodingPreset: webpoptions.EncodingPreset(conf.Hint),
UseSharpYuv: true,
},
)
default:
return errors.New("format not supported")
}
Expand Down Expand Up @@ -229,10 +241,10 @@ func (p *ImageProcessor) Filter(src image.Image, filters ...gift.Filter) (image.
return dst, nil
}

func (p *ImageProcessor) GetDefaultImageConfig(action string) ImageConfig {
func GetDefaultImageConfig(action string) ImageConfig {
return ImageConfig{
Action: action,
Quality: p.Cfg.Cfg.Quality,
Action: action,
Hint: webpoptions.EncodingPresetPhoto,
}
}

Expand All @@ -250,11 +262,13 @@ const (
GIF
TIFF
BMP
WEBP
)

// RequiresDefaultQuality returns if the default quality needs to be applied to images of this format
// RequiresDefaultQuality returns if the default quality needs to be applied to
// images of this format.
func (f Format) RequiresDefaultQuality() bool {
return f == JPEG
return f == JPEG || f == WEBP
}

// SupportsTransparency reports whether it supports transparency in any form.
Expand All @@ -281,6 +295,8 @@ func (f Format) MediaType() media.Type {
return media.TIFFType
case BMP:
return media.BMPType
case WEBP:
return media.WEBPType
default:
panic(fmt.Sprintf("%d is not a valid image format", f))
}
Expand Down
30 changes: 30 additions & 0 deletions resources/images/webp/webp.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright 2021 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.

// +build extended

package webp

import (
"image"
"io"

"github.com/bep/gowebp/libwebp"
"github.com/bep/gowebp/libwebp/webpoptions"
)

// Encode writes the Image m to w in Webp format with the given
// options.
func Encode(w io.Writer, m image.Image, o webpoptions.EncodingOptions) error {
return libwebp.Encode(w, m, o)
}
30 changes: 30 additions & 0 deletions resources/images/webp/webp_notavailable.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright 2021 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.

// +build !extended

package webp

import (
"image"
"io"

"github.com/gohugoio/hugo/common/herrors"

"github.com/bep/gowebp/libwebp/webpoptions"
)

// Encode is only available in the extended version.
func Encode(w io.Writer, m image.Image, o webpoptions.EncodingOptions) error {
return herrors.ErrFeatureNotAvailable
}
Binary file added resources/testdata/sunset.webp
Binary file not shown.

0 comments on commit 30846fc

Please sign in to comment.