Skip to content
Permalink
Browse files

Add a set of image filters

With this you can do variants of this:

```
{{ $img := resources.Get "images/misc/3-jenny.jpg" }}
{{ $img := $img.Resize "300x" }}
{{ $g1 := $img.Filter images.Grayscale }}
{{ $g2 := $img | images.Filter (images.Saturate 30) (images.GaussianBlur 3) }}
```

Fixes #6255
  • Loading branch information...
bep committed Aug 26, 2019
1 parent f9978ed commit 823f53c861bb49aecc6104e0add39fc3b0729025
Showing with 791 additions and 139 deletions.
  1. +1 −1 go.mod
  2. +2 −4 go.sum
  3. +38 −23 resources/image.go
  4. +146 −4 resources/image_test.go
  5. +58 −52 resources/images/config.go
  6. +168 −0 resources/images/filters.go
  7. +77 −13 resources/images/image.go
  8. +214 −0 resources/images/resampling.go
  9. +16 −17 resources/images/smartcrop.go
  10. +17 −14 resources/internal/key.go
  11. +8 −1 resources/internal/key_test.go
  12. +2 −0 resources/resource/resourcetypes.go
  13. BIN resources/testdata/gohugoio24.png
  14. BIN resources/testdata/gohugoio8.png
  15. BIN ...estdata/golden/gohugoio24_huc57dd738f4724f4b341121e66fd85555_267952_100x100_fill_box_center_2.png
  16. BIN .../golden/gohugoio24_huc57dd738f4724f4b341121e66fd85555_267952_14fabac035a010e707ee3733f6590555.png
  17. BIN ...stdata/golden/gohugoio24_huc57dd738f4724f4b341121e66fd85555_267952_200x0_resize_q50_r90_box_2.png
  18. BIN ...ces/testdata/golden/gohugoio24_huc57dd738f4724f4b341121e66fd85555_267952_200x100_resize_box_2.png
  19. BIN ...n/gohugoio24_huc57dd738f4724f4b341121e66fd85555_267952_300x100_fill_nearestneighbor_topleft_2.png
  20. BIN ...ta/golden/gohugoio24_huc57dd738f4724f4b341121e66fd85555_267952_300x200_fill_gaussian_smart1_2.png
  21. BIN ...ces/testdata/golden/gohugoio24_huc57dd738f4724f4b341121e66fd85555_267952_300x200_fit_linear_2.png
  22. BIN ...ata/golden/gohugoio24_huc57dd738f4724f4b341121e66fd85555_267952_400x200_fill_box_bottomleft_2.png
  23. BIN ...estdata/golden/gohugoio24_huc57dd738f4724f4b341121e66fd85555_267952_400x200_fill_box_center_2.png
  24. BIN .../golden/gohugoio24_huc57dd738f4724f4b341121e66fd85555_267952_55b828db27003cb979bac711748f4789.png
  25. BIN ...urces/testdata/golden/gohugoio24_huc57dd738f4724f4b341121e66fd85555_267952_600x0_resize_box_2.png
  26. BIN .../golden/gohugoio24_huc57dd738f4724f4b341121e66fd85555_267952_621ae6f4010e2eb164521f54f653df1f.png
  27. BIN .../golden/gohugoio24_huc57dd738f4724f4b341121e66fd85555_267952_65ffdad1306cecec4d21bac1edd47c44.png
  28. BIN .../golden/gohugoio24_huc57dd738f4724f4b341121e66fd85555_267952_84b0614b9f84c94c0773ef49ae868d0b.png
  29. BIN .../golden/gohugoio24_huc57dd738f4724f4b341121e66fd85555_267952_874d58b1c4b4b538f7ade152b3e57df8.png
  30. BIN .../golden/gohugoio24_huc57dd738f4724f4b341121e66fd85555_267952_958fee7992cf502355355c021148638b.png
  31. BIN .../golden/gohugoio24_huc57dd738f4724f4b341121e66fd85555_267952_9c5c204a4fc82e861344066bc8d0c7db.png
  32. BIN .../golden/gohugoio24_huc57dd738f4724f4b341121e66fd85555_267952_a0088abf33fdbf6be1651a71e7d4dc33.png
  33. BIN .../golden/gohugoio24_huc57dd738f4724f4b341121e66fd85555_267952_cdb3de8b01145d94ba41047655e42695.png
  34. BIN .../golden/gohugoio24_huc57dd738f4724f4b341121e66fd85555_267952_cfc2eacca4b2748852f953954207d615.png
  35. BIN .../golden/gohugoio24_huc57dd738f4724f4b341121e66fd85555_267952_d1ad299f68cb4b3e1eba2ab7633e7857.png
  36. BIN .../golden/gohugoio24_huc57dd738f4724f4b341121e66fd85555_267952_d1f39c78ba8a0ada8233161edeed27ee.png
  37. BIN .../golden/gohugoio24_huc57dd738f4724f4b341121e66fd85555_267952_dd36fa3cc8ae7cf4d686caf1a171284b.png
  38. BIN .../golden/gohugoio24_huc57dd738f4724f4b341121e66fd85555_267952_f5d42d1797f90edd6379e0b082fdd53b.png
  39. BIN .../testdata/golden/gohugoio8_hu7f72c00afdf7634587afaa5eff2a25b2_73538_100x100_fill_box_center_2.png
  40. BIN ...ta/golden/gohugoio8_hu7f72c00afdf7634587afaa5eff2a25b2_73538_1bf2d9610b385893204d0a57ef8d1532.png
  41. BIN ...testdata/golden/gohugoio8_hu7f72c00afdf7634587afaa5eff2a25b2_73538_200x0_resize_q50_r90_box_2.png
  42. BIN ...urces/testdata/golden/gohugoio8_hu7f72c00afdf7634587afaa5eff2a25b2_73538_200x100_resize_box_2.png
  43. BIN ...den/gohugoio8_hu7f72c00afdf7634587afaa5eff2a25b2_73538_300x100_fill_nearestneighbor_topleft_2.png
  44. BIN ...data/golden/gohugoio8_hu7f72c00afdf7634587afaa5eff2a25b2_73538_300x200_fill_gaussian_smart1_2.png
  45. BIN ...urces/testdata/golden/gohugoio8_hu7f72c00afdf7634587afaa5eff2a25b2_73538_300x200_fit_linear_2.png
  46. BIN ...tdata/golden/gohugoio8_hu7f72c00afdf7634587afaa5eff2a25b2_73538_400x200_fill_box_bottomleft_2.png
  47. BIN .../testdata/golden/gohugoio8_hu7f72c00afdf7634587afaa5eff2a25b2_73538_400x200_fill_box_center_2.png
  48. BIN ...ta/golden/gohugoio8_hu7f72c00afdf7634587afaa5eff2a25b2_73538_41369feac467f9ecec9ef46911b04fa1.png
  49. BIN ...ta/golden/gohugoio8_hu7f72c00afdf7634587afaa5eff2a25b2_73538_4c320010919da2d8b63ed24818b4d8e1.png
  50. BIN resources/testdata/golden/gohugoio8_hu7f72c00afdf7634587afaa5eff2a25b2_73538_600x0_resize_box_2.png
  51. BIN ...ta/golden/gohugoio8_hu7f72c00afdf7634587afaa5eff2a25b2_73538_7852bca7fb011b36d030e4d35d8e1d90.png
  52. BIN ...ta/golden/gohugoio8_hu7f72c00afdf7634587afaa5eff2a25b2_73538_798ebb7a9e9dc7edd40e2832eb77e457.png
  53. BIN ...ta/golden/gohugoio8_hu7f72c00afdf7634587afaa5eff2a25b2_73538_84a8d324276a96584446750f06d04bd4.png
  54. BIN ...ta/golden/gohugoio8_hu7f72c00afdf7634587afaa5eff2a25b2_73538_8544b956dc08b714975ae52d4dcfdd78.png
  55. BIN ...ta/golden/gohugoio8_hu7f72c00afdf7634587afaa5eff2a25b2_73538_888208ddeeeb3dcfe84697903ddffe30.png
  56. BIN ...ta/golden/gohugoio8_hu7f72c00afdf7634587afaa5eff2a25b2_73538_9660b4bf59aeb8ac8714d3e466af6197.png
  57. BIN ...ta/golden/gohugoio8_hu7f72c00afdf7634587afaa5eff2a25b2_73538_9a86fee686dd5973923f5ef5c3b0bc74.png
  58. BIN ...ta/golden/gohugoio8_hu7f72c00afdf7634587afaa5eff2a25b2_73538_9d4c2220235b3c2d9fa6506be571560f.png
  59. BIN ...ta/golden/gohugoio8_hu7f72c00afdf7634587afaa5eff2a25b2_73538_bac1f274c6786fdb63dd215df2226cd9.png
  60. BIN ...ta/golden/gohugoio8_hu7f72c00afdf7634587afaa5eff2a25b2_73538_c1ced24877f4b1baf563997e33cadcfa.png
  61. BIN ...ta/golden/gohugoio8_hu7f72c00afdf7634587afaa5eff2a25b2_73538_c74bb417b961e09cf1aac2130b7b9b85.png
  62. BIN ...ta/golden/gohugoio8_hu7f72c00afdf7634587afaa5eff2a25b2_73538_de67126dc370f606d57f2c229b3accab.png
  63. BIN ...tdata/golden/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_0d1b300da7a815ed567b6dadb6f2ce5e.jpg
  64. BIN ...s/testdata/golden/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_100x100_fill_q75_box_center.jpg
  65. BIN ...tdata/golden/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_17fd3c558d78ce249b5f0bcbe1ddbffb.jpg
  66. BIN ...rces/testdata/golden/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_200x0_resize_q50_r90_box.jpg
  67. BIN resources/testdata/golden/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_200x100_resize_q75_box.jpg
  68. BIN ...lden/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_300x100_fill_q75_nearestneighbor_topleft.jpg
  69. BIN ...tdata/golden/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_300x200_fill_q75_gaussian_smart1.jpg
  70. BIN resources/testdata/golden/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_300x200_fit_q75_linear.jpg
  71. BIN ...tdata/golden/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_30fc2aab35ca0861bf396d09aebc85a4.jpg
  72. BIN ...tdata/golden/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_352eb0101b7c88107520ba719432bbb2.jpg
  73. BIN ...tdata/golden/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_3efc2d0f29a8e12c5a690fc6c9288854.jpg
  74. BIN ...tdata/golden/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_3f1b1455c4a7d13c5aeb7510f9a6a581.jpg
  75. BIN ...stdata/golden/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_400x200_fill_q75_box_bottomleft.jpg
  76. BIN ...s/testdata/golden/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_400x200_fill_q75_box_center.jpg
  77. BIN resources/testdata/golden/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_600x0_resize_q75_box.jpg
  78. BIN ...tdata/golden/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_6c5c12ac79d3455ccb1993d51eec3cdf.jpg
  79. BIN ...tdata/golden/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_7d9bc4700565266807dc476421066137.jpg
  80. BIN ...tdata/golden/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_9f00027c376fe8556cc9996c47f23f78.jpg
  81. BIN ...tdata/golden/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_abf356affd7d70d6bec3b3498b572191.jpg
  82. BIN ...tdata/golden/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_c36da6818db1ab630c3f87f65170003b.jpg
  83. BIN ...tdata/golden/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_cb45fcba865177290c89dc9f41d6ff7a.jpg
  84. BIN ...tdata/golden/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_d30c10468b33df9010d185a8fe8f0491.jpg
  85. BIN ...tdata/golden/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_de1fe6c0f40e7165355507d0f1748083.jpg
  86. BIN ...tdata/golden/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_f6d8fe32ce3e83abf130e91e33456914.jpg
  87. +12 −6 resources/testhelpers_test.go
  88. +5 −0 resources/transform.go
  89. +27 −4 tpl/images/images.go
2 go.mod
@@ -12,7 +12,7 @@ require (
github.com/bep/debounce v1.2.0
github.com/bep/gitmap v1.1.0
github.com/bep/go-tocss v0.6.0
github.com/disintegration/imaging v1.6.0
github.com/disintegration/gift v1.2.1
github.com/dustin/go-humanize v1.0.0
github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385
github.com/fortytw2/leaktest v1.3.0
6 go.sum
@@ -88,8 +88,8 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8=
github.com/disintegration/imaging v1.6.0 h1:nVPXRUUQ36Z7MNf0O77UzgnOb1mkMMor7lmJMJXc/mA=
github.com/disintegration/imaging v1.6.0/go.mod h1:xuIt+sRxDFrHS0drzXUlCJthkJ8k7lkkUojDSR247MQ=
github.com/disintegration/gift v1.2.1 h1:Y005a1X4Z7Uc+0gLpSAsKhWi4qLtsdEcMIbbdvdZ6pc=
github.com/disintegration/gift v1.2.1/go.mod h1:Jh2i7f7Q2BM7Ezno3PhfezbR1xpUg9dUg3/RlKGr4HI=
github.com/dlclark/regexp2 v1.1.6 h1:CqB4MjHw0MFCDj+PHHjiESmHX+N7t0tJzKvC6M97BRg=
github.com/dlclark/regexp2 v1.1.6/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
@@ -338,8 +338,6 @@ golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnf
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190422183909-d864b10871cd/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81 h1:00VmoueYNlNz/aHIilyyQz/MHSqGoWJzpFv/HW8xpzI=
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
golang.org/x/image v0.0.0-20190523035834-f03afa92d3ff h1:+2zgJKVDVAz/BWSsuniCmU1kLCjL88Z8/kv39xCI9NQ=
golang.org/x/image v0.0.0-20190523035834-f03afa92d3ff/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -16,18 +16,19 @@ package resources
import (
"fmt"
"image"
"image/color"
"image/draw"
_ "image/gif"
_ "image/png"
"os"
"strings"

"github.com/gohugoio/hugo/resources/internal"

"github.com/gohugoio/hugo/resources/resource"

_errors "github.com/pkg/errors"

"github.com/disintegration/imaging"
"github.com/disintegration/gift"
"github.com/gohugoio/hugo/helpers"
"github.com/gohugoio/hugo/resources/images"

@@ -82,25 +83,49 @@ func (i *imageResource) cloneWithUpdates(u *transformationUpdate) (baseResource,
// filter and returns the transformed image. If one of width or height is 0, the image aspect
// ratio is preserved.
func (i *imageResource) Resize(spec string) (resource.Image, error) {
return i.doWithImageConfig("resize", spec, func(src image.Image, conf images.ImageConfig) (image.Image, error) {
return i.Proc.Resize(src, conf)
conf, err := i.decodeImageConfig("resize", spec)
if err != nil {
return nil, err
}

return i.doWithImageConfig(conf, func(src image.Image) (image.Image, error) {
return i.Proc.ApplyFiltersFromConfig(src, conf)
})
}

// Fit scales down the image using the specified resample filter to fit the specified
// maximum width and height.
func (i *imageResource) Fit(spec string) (resource.Image, error) {
return i.doWithImageConfig("fit", spec, func(src image.Image, conf images.ImageConfig) (image.Image, error) {
return i.Proc.Fit(src, conf)
conf, err := i.decodeImageConfig("fit", spec)
if err != nil {
return nil, err
}

return i.doWithImageConfig(conf, func(src image.Image) (image.Image, error) {
return i.Proc.ApplyFiltersFromConfig(src, conf)
})
}

// Fill scales the image to the smallest possible size that will cover the specified dimensions,
// crops the resized image to the specified dimensions using the given anchor point.
// Space delimited config: 200x300 TopLeft
func (i *imageResource) Fill(spec string) (resource.Image, error) {
return i.doWithImageConfig("fill", spec, func(src image.Image, conf images.ImageConfig) (image.Image, error) {
return i.Proc.Fill(src, conf)
conf, err := i.decodeImageConfig("fill", spec)
if err != nil {
return nil, err
}

return i.doWithImageConfig(conf, func(src image.Image) (image.Image, error) {
return i.Proc.ApplyFiltersFromConfig(src, conf)
})
}

func (i *imageResource) Filter(filters ...gift.Filter) (resource.Image, error) {
conf := i.Proc.GetDefaultImageConfig("filter")
conf.Key = internal.HashString(filters)

return i.doWithImageConfig(conf, func(src image.Image) (image.Image, error) {
return i.Proc.Filter(src, filters...)
})
}

@@ -118,37 +143,27 @@ const imageProcWorkers = 1

var imageProcSem = make(chan bool, imageProcWorkers)

func (i *imageResource) doWithImageConfig(action, spec string, f func(src image.Image, conf images.ImageConfig) (image.Image, error)) (resource.Image, error) {
conf, err := i.decodeImageConfig(action, spec)
if err != nil {
return nil, err
}

func (i *imageResource) doWithImageConfig(conf images.ImageConfig, f func(src image.Image) (image.Image, error)) (resource.Image, error) {
return i.getSpec().imageCache.getOrCreate(i, conf, func() (*imageResource, image.Image, error) {
imageProcSem <- true
defer func() {
<-imageProcSem
}()

errOp := action
errOp := conf.Action
errPath := i.getSourceFilename()

src, err := i.decodeSource()
if err != nil {
return nil, nil, &os.PathError{Op: errOp, Path: errPath, Err: err}
}

if conf.Rotate != 0 {
// Rotate it before any scaling to get the dimensions correct.
src = imaging.Rotate(src, float64(conf.Rotate), color.Transparent)
}

converted, err := f(src, conf)
converted, err := f(src)
if err != nil {
return nil, nil, &os.PathError{Op: errOp, Path: errPath, Err: err}
}

if i.Format == imaging.PNG {
if i.Format == images.PNG {
// Apply the colour palette from the source
if paletted, ok := src.(*image.Paletted); ok {
tmp := image.NewPaletted(converted.Bounds(), paletted.Palette)
@@ -222,7 +237,7 @@ func (i *imageResource) relTargetPathFromConfig(conf images.ImageConfig) dirFile
// Do not change for no good reason.
const md5Threshold = 100

key := conf.Key(i.Format)
key := conf.GetKey(i.Format)

// It is useful to have the key in clear text, but when nesting transforms, it
// can easily be too long to read, and maybe even too long
@@ -16,14 +16,20 @@ package resources
import (
"fmt"
"math/rand"
"os"
"path/filepath"
"regexp"
"strconv"
"sync"
"testing"

"github.com/disintegration/gift"

"github.com/gohugoio/hugo/helpers"

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

"github.com/google/go-cmp/cmp"

"github.com/gohugoio/hugo/htesting/hqt"
@@ -35,6 +41,9 @@ var eq = qt.CmpEquals(
cmp.Comparer(func(p1, p2 *resourceAdapter) bool {
return p1.resourceAdapterInner == p2.resourceAdapterInner
}),
cmp.Comparer(func(p1, p2 os.FileInfo) bool {
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(m1, m2 media.Type) bool {
return m1.Type() == m2.Type()
@@ -94,7 +103,7 @@ func TestImageTransformBasic(t *testing.T) {
fittedAgain, err = fittedAgain.Fit("10x20")
c.Assert(err, qt.IsNil)
c.Assert(fittedAgain.RelPermalink(), qt.Equals, "/a/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_3f65ba24dc2b7fba0f56d7f104519157.jpg")
assertWidthHeight(fittedAgain, 10, 6)
assertWidthHeight(fittedAgain, 10, 7)

filled, err := image.Fill("200x100 bottomLeft")
c.Assert(err, qt.IsNil)
@@ -155,7 +164,10 @@ func TestImagePermalinkPublishOrder(t *testing.T) {

t.Run(name, func(t *testing.T) {
c := qt.New(t)
spec := newTestResourceOsFs(c)
spec, workDir := newTestResourceOsFs(c)
defer func() {
os.Remove(workDir)
}()

check1 := func(img resource.Image) {
resizedLink := "/a/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_100x50_resize_q75_box.jpg"
@@ -192,7 +204,10 @@ func TestImageTransformConcurrent(t *testing.T) {

c := qt.New(t)

spec := newTestResourceOsFs(c)
spec, workDir := newTestResourceOsFs(c)
defer func() {
os.Remove(workDir)
}()

image := fetchImageForSpec(spec, c, "sunset.jpg")

@@ -317,6 +332,133 @@ func TestSVGImageContent(t *testing.T) {
c.Assert(content.(string), qt.Contains, `<svg height="100" width="100">`)
}

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

devMode := false

testImages := []string{"sunset.jpg", "gohugoio8.png", "gohugoio24.png"}

spec, workDir := newTestResourceOsFs(c)
defer func() {
if !devMode {
os.Remove(workDir)
}
}()

if devMode {
fmt.Println(workDir)
}

for _, img := range testImages {

orig := fetchImageForSpec(spec, c, img)
for _, resizeSpec := range []string{"200x100", "600x", "200x r90 q50 Box"} {
resized, err := orig.Resize(resizeSpec)
c.Assert(err, qt.IsNil)
rel := resized.RelPermalink()
c.Log("resize", rel)
c.Assert(rel, qt.Not(qt.Equals), "")
}

for _, fillSpec := range []string{"300x200 Gaussian Smart", "100x100 Center", "300x100 TopLeft NearestNeighbor", "400x200 BottomLeft"} {
resized, err := orig.Fill(fillSpec)
c.Assert(err, qt.IsNil)
rel := resized.RelPermalink()
c.Log("fill", rel)
c.Assert(rel, qt.Not(qt.Equals), "")
}

for _, fitSpec := range []string{"300x200 Linear"} {
resized, err := orig.Fit(fitSpec)
c.Assert(err, qt.IsNil)
rel := resized.RelPermalink()
c.Log("fit", rel)
c.Assert(rel, qt.Not(qt.Equals), "")
}

f := &images.Filters{}

filters := []gift.Filter{
f.Grayscale(),
f.GaussianBlur(6),
f.Saturation(50),
f.Sepia(100),
f.Brightness(30),
f.ColorBalance(10, -10, -10),
f.Colorize(240, 50, 100),
f.Gamma(1.5),
f.UnsharpMask(1, 1, 0),
f.Sigmoid(0.5, 7),
f.Pixelate(5),
f.Invert(),
f.Hue(22),
f.Contrast(32.5),
}

resized, err := orig.Fill("400x200 center")

for _, filter := range filters {
resized, err := resized.Filter(filter)
c.Assert(err, qt.IsNil)
rel := resized.RelPermalink()
c.Logf("filter: %v %s", filter, rel)
c.Assert(rel, qt.Not(qt.Equals), "")
}

resized, err = resized.Filter(filters[0:4]...)
c.Assert(err, qt.IsNil)
rel := resized.RelPermalink()
c.Log("filter all", rel)
c.Assert(rel, qt.Not(qt.Equals), "")
}

if devMode {
return
}

dir1 := filepath.Join(workDir, "resources/_gen/images/a")
dir2 := filepath.FromSlash("testdata/golden")

// The two dirs above should now be the same.
d1, err := os.Open(dir1)
c.Assert(err, qt.IsNil)
d2, err := os.Open(dir2)
c.Assert(err, qt.IsNil)

dirinfos1, err := d1.Readdir(-1)
c.Assert(err, qt.IsNil)
dirinfos2, err := d2.Readdir(-1)

c.Assert(err, qt.IsNil)
c.Assert(len(dirinfos1), qt.Equals, len(dirinfos2))

for i, fi1 := range dirinfos1 {
if regexp.MustCompile("gauss").MatchString(fi1.Name()) {
continue
}
fi2 := dirinfos2[i]
c.Assert(fi1.Name(), qt.Equals, fi2.Name())
c.Assert(fi1, eq, fi2)
f1, err := os.Open(filepath.Join(dir1, fi1.Name()))
c.Assert(err, qt.IsNil)
f2, err := os.Open(filepath.Join(dir2, fi2.Name()))
c.Assert(err, qt.IsNil)

hash1, err := helpers.MD5FromReader(f1)
c.Assert(err, qt.IsNil)
hash2, err := helpers.MD5FromReader(f2)
c.Assert(err, qt.IsNil)

f1.Close()
f2.Close()

c.Assert(hash1, qt.Equals, hash2)
}

}

func BenchmarkResizeParallel(b *testing.B) {
c := qt.New(b)
img := fetchSunset(c)

1 comment on commit 823f53c

@divinerites

This comment has been minimized.

Copy link

commented on 823f53c Aug 29, 2019

In your example should be (images.Saturation 30).

Seems really really promising ! thanks Bep.

Please sign in to comment.
You can’t perform that action at this time.