From 1f5a15aba0fc7cc4f4cf4ce1afc8d6db81c63e06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Thu, 16 Jan 2025 19:15:30 +0100 Subject: [PATCH] resources: Fix 2 image file cache key issues * Always include the content hash in the cache key for unprocessed images. * Always include the image config hash in the cache key. This is also a major cleanup/simplification of the implementation in this area. Note that this, unfortunately, forces new hashes/filenames for generated images. Fixes #13273 Fixes #13272 --- config/namespace.go | 2 +- config/namespace_test.go | 2 +- hugolib/hugo_sites_multihost_test.go | 4 +- hugolib/image_test.go | 16 +- .../pagesfromgotmpl_integration_test.go | 2 +- hugolib/resource_chain_test.go | 4 +- resources/image.go | 96 ++++-------- resources/image_cache.go | 2 +- resources/image_extended_test.go | 2 +- resources/image_test.go | 26 ++-- resources/images/config.go | 127 +++++++--------- resources/images/config_test.go | 28 ++-- resources/images/filters.go | 5 +- resources/images/image.go | 8 +- .../images/images_golden_integration_test.go | 142 ++++++++++++++++++ resources/images/imagetesting/testing.go | 25 ++- resources/images/smartcrop.go | 6 +- .../images_golden/filters/mask2/green.jpg | Bin 0 -> 8811 bytes .../images_golden/filters/mask2/pink.jpg | Bin 0 -> 6147 bytes .../methods/crop-sunsetjpg-200x200.jpg | Bin 0 -> 2524 bytes .../crop-sunsetjpg-350x400-center-q20.jpg | Bin 0 -> 4026 bytes .../crop-sunsetjpg-350x400-center-r90.jpg | Bin 0 -> 8499 bytes .../methods/crop-sunsetjpg-350x400-center.jpg | Bin 0 -> 8619 bytes .../methods/crop-sunsetjpg-350x400-smart.jpg | Bin 0 -> 8628 bytes .../methods/fill-sunsetjpg-90x120-left.jpg | Bin 0 -> 1668 bytes .../methods/fill-sunsetjpg-90x120-right.jpg | Bin 0 -> 1631 bytes .../methods/fit-sunsetjpg-200x200.jpg | Bin 0 -> 2877 bytes .../resize-gopherpng-100x-03fc56-jpg.jpg | Bin 0 -> 3181 bytes .../methods/resize-gopherpng-100x-fc03ec.png | Bin 0 -> 3209 bytes .../methods/resize-gopherpng-100x.png | Bin 0 -> 3528 bytes .../methods/resize-sunsetjpg-300x.jpg | Bin 0 -> 4950 bytes .../methods/resize-sunsetjpg-x200.jpg | Bin 0 -> 5434 bytes resources/resource.go | 5 + resources/resource_spec.go | 42 +++--- resources/resources_integration_test.go | 12 +- resources/testdata/mask2.png | Bin 0 -> 36823 bytes resources/transform_test.go | 15 +- tpl/resources/resources_integration_test.go | 2 +- 38 files changed, 341 insertions(+), 232 deletions(-) create mode 100644 resources/images/testdata/images_golden/filters/mask2/green.jpg create mode 100644 resources/images/testdata/images_golden/filters/mask2/pink.jpg create mode 100644 resources/images/testdata/images_golden/methods/crop-sunsetjpg-200x200.jpg create mode 100644 resources/images/testdata/images_golden/methods/crop-sunsetjpg-350x400-center-q20.jpg create mode 100644 resources/images/testdata/images_golden/methods/crop-sunsetjpg-350x400-center-r90.jpg create mode 100644 resources/images/testdata/images_golden/methods/crop-sunsetjpg-350x400-center.jpg create mode 100644 resources/images/testdata/images_golden/methods/crop-sunsetjpg-350x400-smart.jpg create mode 100644 resources/images/testdata/images_golden/methods/fill-sunsetjpg-90x120-left.jpg create mode 100644 resources/images/testdata/images_golden/methods/fill-sunsetjpg-90x120-right.jpg create mode 100644 resources/images/testdata/images_golden/methods/fit-sunsetjpg-200x200.jpg create mode 100644 resources/images/testdata/images_golden/methods/resize-gopherpng-100x-03fc56-jpg.jpg create mode 100644 resources/images/testdata/images_golden/methods/resize-gopherpng-100x-fc03ec.png create mode 100644 resources/images/testdata/images_golden/methods/resize-gopherpng-100x.png create mode 100644 resources/images/testdata/images_golden/methods/resize-sunsetjpg-300x.jpg create mode 100644 resources/images/testdata/images_golden/methods/resize-sunsetjpg-x200.jpg create mode 100644 resources/testdata/mask2.png diff --git a/config/namespace.go b/config/namespace.go index 46b5014c34c..e41b56e2d9e 100644 --- a/config/namespace.go +++ b/config/namespace.go @@ -22,7 +22,7 @@ import ( func DecodeNamespace[S, C any](configSource any, buildConfig func(any) (C, any, error)) (*ConfigNamespace[S, C], error) { // Calculate the hash of the input (not including any defaults applied later). // This allows us to introduce new config options without breaking the hash. - h := hashing.HashString(configSource) + h := hashing.HashStringHex(configSource) // Build the config c, ext, err := buildConfig(configSource) diff --git a/config/namespace_test.go b/config/namespace_test.go index 5eacdeac7dd..df27ae05cf0 100644 --- a/config/namespace_test.go +++ b/config/namespace_test.go @@ -43,7 +43,7 @@ func TestNamespace(t *testing.T) { c.Assert(err, qt.IsNil) c.Assert(ns, qt.Not(qt.IsNil)) c.Assert(ns.SourceStructure, qt.DeepEquals, map[string]interface{}{"foo": "bar"}) - c.Assert(ns.SourceHash, qt.Equals, "1450430416588600409") + c.Assert(ns.SourceHash, qt.Equals, "1420f6c7782f7459") c.Assert(ns.Config, qt.DeepEquals, &tstNsExt{Foo: "bar"}) c.Assert(ns.Signature(), qt.DeepEquals, []*tstNsExt(nil)) } diff --git a/hugolib/hugo_sites_multihost_test.go b/hugolib/hugo_sites_multihost_test.go index 39504202b2b..37f7ab92791 100644 --- a/hugolib/hugo_sites_multihost_test.go +++ b/hugolib/hugo_sites_multihost_test.go @@ -205,9 +205,9 @@ title: mybundle-en b.AssertFileExists("public/de/mybundle/pixel.png", true) b.AssertFileExists("public/en/mybundle/pixel.png", true) - b.AssertFileExists("public/de/mybundle/pixel_hu8581513846771248023.png", true) + b.AssertFileExists("public/de/mybundle/pixel_hu_58204cbc58507d74.png", true) // failing test below - b.AssertFileExists("public/en/mybundle/pixel_hu8581513846771248023.png", true) + b.AssertFileExists("public/en/mybundle/pixel_hu_58204cbc58507d74.png", true) } func TestMultihostResourceOneBaseURLWithSuPath(t *testing.T) { diff --git a/hugolib/image_test.go b/hugolib/image_test.go index 7dcd9fc26ab..09a5b841e90 100644 --- a/hugolib/image_test.go +++ b/hugolib/image_test.go @@ -72,20 +72,20 @@ SUNSET2: {{ $resized2.RelPermalink }}/{{ $resized2.Width }}/Lat: {{ $resized2.Ex b.Build(BuildCfg{}) - b.AssertFileContent("public/index.html", "SUNSET FOR: en: /bundle/sunset_hu13235715490294913361.jpg/200/Lat: 36.59744166666667") - b.AssertFileContent("public/fr/index.html", "SUNSET FOR: fr: /bundle/sunset_hu13235715490294913361.jpg/200/Lat: 36.59744166666667") - b.AssertFileContent("public/index.html", " SUNSET2: /images/sunset_hu1573057890424052540.jpg/123/Lat: 36.59744166666667") - b.AssertFileContent("public/nn/index.html", " SUNSET2: /images/sunset_hu1573057890424052540.jpg/123/Lat: 36.59744166666667") + b.AssertFileContent("public/index.html", "SUNSET FOR: en: /bundle/sunset_hu_77061c65c31d2244.jpg/200/Lat: 36.59744166666667") + b.AssertFileContent("public/fr/index.html", "SUNSET FOR: fr: /bundle/sunset_hu_77061c65c31d2244.jpg/200/Lat: 36.59744166666667") + b.AssertFileContent("public/index.html", " SUNSET2: /images/sunset_hu_b52e3343ea6a8764.jpg/123/Lat: 36.59744166666667") + b.AssertFileContent("public/nn/index.html", " SUNSET2: /images/sunset_hu_b52e3343ea6a8764.jpg/123/Lat: 36.59744166666667") - b.AssertImage(200, 200, "public/bundle/sunset_hu13235715490294913361.jpg") + b.AssertImage(200, 200, "public/bundle/sunset_hu_77061c65c31d2244.jpg") // Check the file cache - b.AssertImage(200, 200, "resources/_gen/images/bundle/sunset_hu13235715490294913361.jpg") + b.AssertImage(200, 200, "resources/_gen/images/bundle/sunset_hu_77061c65c31d2244.jpg") - b.AssertFileContent("resources/_gen/images/bundle/sunset_17710516992648092201.json", + b.AssertFileContent("resources/_gen/images/bundle/sunset_d209dcdc6b875e26.json", "FocalLengthIn35mmFormat|uint16", "PENTAX") - b.AssertFileContent("resources/_gen/images/images/sunset_17710516992648092201.json", + b.AssertFileContent("resources/_gen/images/images/sunset_d209dcdc6b875e26.json", "FocalLengthIn35mmFormat|uint16", "PENTAX") b.AssertNoDuplicateWrites() diff --git a/hugolib/pagesfromdata/pagesfromgotmpl_integration_test.go b/hugolib/pagesfromdata/pagesfromgotmpl_integration_test.go index b033aad2b65..64ee7039731 100644 --- a/hugolib/pagesfromdata/pagesfromgotmpl_integration_test.go +++ b/hugolib/pagesfromdata/pagesfromgotmpl_integration_test.go @@ -119,7 +119,7 @@ docs/p1/sub/mymixcasetext2.txt "RelPermalink: /docs/p1/sub/mymixcasetext2.txt|Name: sub/mymixcasetext2.txt|", "RelPermalink: /mydata.yaml|Name: sub/data1.yaml|Title: Sub data|Params: map[]|", "Featured Image: /a/pixel.png|featured.png|", - "Resized Featured Image: /a/pixel_hu16809842526914527184.png|10|", + "Resized Featured Image: /a/pixel_hu_a32b3e361d55df1.png|10|", // Resource from string "RelPermalink: /docs/p1/mytext.txt|Name: textresource|Title: My Text Resource|Params: map[param1:param1v]|", // Dates diff --git a/hugolib/resource_chain_test.go b/hugolib/resource_chain_test.go index 669114c8a4a..942873ae4c9 100644 --- a/hugolib/resource_chain_test.go +++ b/hugolib/resource_chain_test.go @@ -106,12 +106,12 @@ FAILED REMOTE ERROR DETAILS CONTENT: {{ with $failedImg }}{{ with .Err }}{{ with b.AssertFileContent("public/index.html", fmt.Sprintf(` SUNSET: /images/sunset.jpg|/images/sunset.a9bf1d944e19c0f382e0d8f51de690f7d0bc8fa97390c4242a86c3e5c0737e71.jpg|900|90587 -FIT: /images/sunset.jpg|/images/sunset_hu15210517121918042184.jpg|200 +FIT: /images/sunset.jpg|/images/sunset_hu_f2aae87288f3c13b.jpg|200 CSS integrity Data first: sha256-od9YaHw8nMOL8mUy97Sy8sKwMV3N4hI3aVmZXATxH+8= /styles.min.a1df58687c3c9cc38bf26532f7b4b2f2c2b0315dcde212376959995c04f11fef.css CSS integrity Data last: /styles2.min.1cfc52986836405d37f9998a63fd6dd8608e8c410e5e3db1daaa30f78bc273ba.css sha256-HPxSmGg2QF03+ZmKY/1t2GCOjEEOXj2x2qow94vCc7o= SUNSET REMOTE: /sunset_%[1]s.jpg|/sunset_%[1]s.a9bf1d944e19c0f382e0d8f51de690f7d0bc8fa97390c4242a86c3e5c0737e71.jpg|900|90587 -FIT REMOTE: /sunset_%[1]s.jpg|/sunset_%[1]s_hu15210517121918042184.jpg|200 +FIT REMOTE: /sunset_%[1]s.jpg|/sunset_%[1]s_hu_f2aae87288f3c13b.jpg|200 REMOTE NOT FOUND: OK LOCAL NOT FOUND: OK PRINT PROTOCOL ERROR DETAILS: Err: template: index.html:22:36: executing "index.html" at : error calling GetRemote: Get "gopher://example.org": unsupported protocol scheme "gopher"| diff --git a/resources/image.go b/resources/image.go index 686f70e274d..c1f107b592f 100644 --- a/resources/image.go +++ b/resources/image.go @@ -30,7 +30,6 @@ import ( "github.com/gohugoio/hugo/cache/filecache" "github.com/gohugoio/hugo/common/hashing" - "github.com/gohugoio/hugo/common/hstrings" "github.com/gohugoio/hugo/common/paths" "github.com/disintegration/gift" @@ -205,15 +204,12 @@ func (i *imageResource) cloneWithUpdates(u *transformationUpdate) (baseResource, }, nil } -var imageActions = []string{images.ActionResize, images.ActionCrop, images.ActionFit, images.ActionFill} - // Process processes the image with the given spec. // The spec can contain an optional action, one of "resize", "crop", "fit" or "fill". // This makes this method a more flexible version that covers all of Resize, Crop, Fit and Fill, // but it also supports e.g. format conversions without any resize action. func (i *imageResource) Process(spec string) (images.ImageResource, error) { - action, options := i.resolveActionOptions(spec) - return i.processActionOptions(action, options) + return i.processActionSpec("", spec) } // Resize resizes the image to the specified width and height using the specified resampling @@ -243,7 +239,7 @@ func (i *imageResource) Fill(spec string) (images.ImageResource, error) { } func (i *imageResource) Filter(filters ...any) (images.ImageResource, error) { - var conf images.ImageConfig + var confMain images.ImageConfig var gfilters []gift.Filter @@ -251,47 +247,30 @@ func (i *imageResource) Filter(filters ...any) (images.ImageResource, error) { gfilters = append(gfilters, images.ToFilters(f)...) } - var ( - targetFormat images.Format - configSet bool - ) + var options []string + for _, f := range gfilters { f = images.UnwrapFilter(f) if specProvider, ok := f.(images.ImageProcessSpecProvider); ok { - action, options := i.resolveActionOptions(specProvider.ImageProcessSpec()) - var err error - conf, err = images.DecodeImageConfig(action, options, i.Proc.Cfg, i.Format) - if err != nil { - return nil, err - } - configSet = true - if conf.TargetFormat != 0 { - targetFormat = conf.TargetFormat - // We only support one target format, but prefer the last one, - // so we keep going. - } + options = append(options, strings.Fields(specProvider.ImageProcessSpec())...) } } - if !configSet { - conf = images.GetDefaultImageConfig("filter", i.Proc.Cfg) + confMain, err := images.DecodeImageConfig(options, i.Proc.Cfg, i.Format) + if err != nil { + return nil, err } - conf.Action = "filter" - conf.Key = hashing.HashString(gfilters) - conf.TargetFormat = targetFormat - if conf.TargetFormat == 0 { - conf.TargetFormat = i.Format - } + confMain.Action = "filter" + confMain.Key = hashing.HashString(gfilters) - return i.doWithImageConfig(conf, func(src image.Image) (image.Image, error) { + return i.doWithImageConfig(confMain, func(src image.Image) (image.Image, error) { var filters []gift.Filter for _, f := range gfilters { f = images.UnwrapFilter(f) if specProvider, ok := f.(images.ImageProcessSpecProvider); ok { - processSpec := specProvider.ImageProcessSpec() - action, options := i.resolveActionOptions(processSpec) - conf, err := images.DecodeImageConfig(action, options, i.Proc.Cfg, i.Format) + options := strings.Fields(specProvider.ImageProcessSpec()) + conf, err := images.DecodeImageConfig(options, i.Proc.Cfg, i.Format) if err != nil { return nil, err } @@ -313,25 +292,13 @@ func (i *imageResource) Filter(filters ...any) (images.ImageResource, error) { }) } -func (i *imageResource) resolveActionOptions(spec string) (string, []string) { - var action string - options := strings.Fields(spec) - for i, p := range options { - if hstrings.InSlicEqualFold(imageActions, p) { - action = p - options = append(options[:i], options[i+1:]...) - break - } - } - return action, options -} - func (i *imageResource) processActionSpec(action, spec string) (images.ImageResource, error) { - return i.processActionOptions(action, strings.Fields(spec)) + options := append([]string{action}, strings.Fields(strings.ToLower(spec))...) + return i.processOptions(options) } -func (i *imageResource) processActionOptions(action string, options []string) (images.ImageResource, error) { - conf, err := images.DecodeImageConfig(action, options, i.Proc.Cfg, i.Format) +func (i *imageResource) processOptions(options []string) (images.ImageResource, error) { + conf, err := images.DecodeImageConfig(options, i.Proc.Cfg, i.Format) if err != nil { return nil, err } @@ -343,13 +310,12 @@ func (i *imageResource) processActionOptions(action string, options []string) (i return nil, err } - if action == images.ActionFill { - if conf.Anchor == 0 && img.Width() == 0 || img.Height() == 0 { + if conf.Action == images.ActionFill { + if conf.Anchor == images.SmartCropAnchor && img.Width() == 0 || img.Height() == 0 { // See https://github.com/gohugoio/hugo/issues/7955 // Smartcrop fails silently in some rare cases. // Fall back to a center fill. - conf.Anchor = gift.CenterAnchor - conf.AnchorStr = "center" + conf = conf.Reanchor(gift.CenterAnchor) return i.doWithImageConfig(conf, func(src image.Image) (image.Image, error) { return i.Proc.ApplyFiltersFromConfig(src, conf) }) @@ -417,7 +383,7 @@ func (i *imageResource) doWithImageConfig(conf images.ImageConfig, f func(src im } ci := i.clone(converted) - targetPath := i.relTargetPathFromConfig(conf) + targetPath := i.relTargetPathFromConfig(conf, i.getSpec().imaging.Cfg.SourceHash) ci.setTargetPath(targetPath) ci.Format = conf.TargetFormat ci.setMediaType(conf.TargetFormat.MediaType()) @@ -485,26 +451,30 @@ func (i *imageResource) getImageMetaCacheTargetPath() string { df := i.getResourcePaths() p1, _ := paths.FileAndExt(df.File) h := i.hash() - idStr := hashing.HashString(h, i.size(), imageMetaVersionNumber, cfgHash) + idStr := hashing.HashStringHex(h, i.size(), imageMetaVersionNumber, cfgHash) df.File = fmt.Sprintf("%s_%s.json", p1, idStr) return df.TargetPath() } -func (i *imageResource) relTargetPathFromConfig(conf images.ImageConfig) internal.ResourcePaths { +func (i *imageResource) relTargetPathFromConfig(conf images.ImageConfig, imagingConfigSourceHash string) internal.ResourcePaths { p1, p2 := paths.FileAndExt(i.getResourcePaths().File) if conf.TargetFormat != i.Format { p2 = conf.TargetFormat.DefaultExtension() } - const prefix = "_hu" - huIdx := strings.LastIndex(p1, prefix) - incomingID := "i" + + // Do not change. + const imageHashPrefix = "_hu_" + + huIdx := strings.LastIndex(p1, imageHashPrefix) + incomingID := "" if huIdx > -1 { - incomingID = p1[huIdx+len(prefix):] + incomingID = p1[huIdx+len(imageHashPrefix):] p1 = p1[:huIdx] } - hash := hashing.HashUint64(incomingID, i.hash(), conf.GetKey(i.Format)) + + hash := hashing.HashStringHex(incomingID, i.hash(), conf.Key, imagingConfigSourceHash) rp := i.getResourcePaths() - rp.File = fmt.Sprintf("%s%s%d%s", p1, prefix, hash, p2) + rp.File = fmt.Sprintf("%s%s%s%s", p1, imageHashPrefix, hash, p2) return rp } diff --git a/resources/image_cache.go b/resources/image_cache.go index d824c5d1a06..1fc7226095e 100644 --- a/resources/image_cache.go +++ b/resources/image_cache.go @@ -37,7 +37,7 @@ func (c *ImageCache) getOrCreate( parent *imageResource, conf images.ImageConfig, createImage func() (*imageResource, image.Image, error), ) (*resourceAdapter, error) { - relTarget := parent.relTargetPathFromConfig(conf) + relTarget := parent.relTargetPathFromConfig(conf, parent.getSpec().imaging.Cfg.SourceHash) relTargetPath := relTarget.TargetPath() memKey := relTargetPath diff --git a/resources/image_extended_test.go b/resources/image_extended_test.go index e570c4a3d24..f481be9bb40 100644 --- a/resources/image_extended_test.go +++ b/resources/image_extended_test.go @@ -42,6 +42,6 @@ func TestImageResizeWebP(t *testing.T) { resized, err := image.Resize("123x") c.Assert(err, qt.IsNil) c.Assert(image.MediaType(), qt.Equals, media.Builtin.WEBPType) - c.Assert(resized.RelPermalink(), qt.Equals, "/a/sunrise_hu544374262273649331.webp") + c.Assert(resized.RelPermalink(), qt.Equals, "/a/sunrise_hu_a1deb893888915d9.webp") c.Assert(resized.Width(), qt.Equals, 123) } diff --git a/resources/image_test.go b/resources/image_test.go index 5639d457e07..1ba5a149a32 100644 --- a/resources/image_test.go +++ b/resources/image_test.go @@ -113,28 +113,28 @@ func TestImageTransformBasic(t *testing.T) { assertWidthHeight(resizedAndRotated, 125, 200) assertWidthHeight(resized, 300, 200) - c.Assert(resized.RelPermalink(), qt.Equals, "/a/sunset_hu2082030801149749592.jpg") + c.Assert(resized.RelPermalink(), qt.Equals, "/a/sunset_hu_d2115125d9324a79.jpg") fitted, err := resized.Fit("50x50") c.Assert(err, qt.IsNil) - c.Assert(fitted.RelPermalink(), qt.Equals, "/a/sunset_hu16263619592447877226.jpg") + c.Assert(fitted.RelPermalink(), qt.Equals, "/a/sunset_hu_c2c98e06123b048e.jpg") assertWidthHeight(fitted, 50, 33) // Check the MD5 key threshold fittedAgain, _ := fitted.Fit("10x20") fittedAgain, err = fittedAgain.Fit("10x20") c.Assert(err, qt.IsNil) - c.Assert(fittedAgain.RelPermalink(), qt.Equals, "/a/sunset_hu847809310637164306.jpg") + c.Assert(fittedAgain.RelPermalink(), qt.Equals, "/a/sunset_hu_dc9e89c10109de72.jpg") assertWidthHeight(fittedAgain, 10, 7) filled, err := image.Fill("200x100 bottomLeft") c.Assert(err, qt.IsNil) - c.Assert(filled.RelPermalink(), qt.Equals, "/a/sunset_hu18289448341423092707.jpg") + c.Assert(filled.RelPermalink(), qt.Equals, "/a/sunset_hu_b9f6d350738928fe.jpg") assertWidthHeight(filled, 200, 100) smart, err := image.Fill("200x100 smart") c.Assert(err, qt.IsNil) - c.Assert(smart.RelPermalink(), qt.Equals, "/a/sunset_hu11649371610839769766.jpg") + c.Assert(smart.RelPermalink(), qt.Equals, "/a/sunset_hu_6fd390e7b0d26f0b.jpg") assertWidthHeight(smart, 200, 100) // Check cache @@ -144,12 +144,12 @@ func TestImageTransformBasic(t *testing.T) { cropped, err := image.Crop("300x300 topRight") c.Assert(err, qt.IsNil) - c.Assert(cropped.RelPermalink(), qt.Equals, "/a/sunset_hu2242042514052853140.jpg") + c.Assert(cropped.RelPermalink(), qt.Equals, "/a/sunset_hu_3df036e11f4ddd43.jpg") assertWidthHeight(cropped, 300, 300) smartcropped, err := image.Crop("200x200 smart") c.Assert(err, qt.IsNil) - c.Assert(smartcropped.RelPermalink(), qt.Equals, "/a/sunset_hu12983255101170993571.jpg") + c.Assert(smartcropped.RelPermalink(), qt.Equals, "/a/sunset_hu_12e2d26de89b464b.jpg") assertWidthHeight(smartcropped, 200, 200) // Check cache @@ -216,7 +216,7 @@ func TestImageTransformFormat(t *testing.T) { imagePng, err := image.Resize("450x png") c.Assert(err, qt.IsNil) - c.Assert(imagePng.RelPermalink(), qt.Equals, "/a/sunset_hu11737890885216583918.png") + c.Assert(imagePng.RelPermalink(), qt.Equals, "/a/sunset_hu_e8b9444dcf2e75ef.png") c.Assert(imagePng.ResourceType(), qt.Equals, "image") assertExtWidthHeight(imagePng, ".png", 450, 281) c.Assert(imagePng.Name(), qt.Equals, "sunset.jpg") @@ -224,7 +224,7 @@ func TestImageTransformFormat(t *testing.T) { imageGif, err := image.Resize("225x gif") c.Assert(err, qt.IsNil) - c.Assert(imageGif.RelPermalink(), qt.Equals, "/a/sunset_hu1431827106749674475.gif") + c.Assert(imageGif.RelPermalink(), qt.Equals, "/a/sunset_hu_f80842d4c3789345.gif") c.Assert(imageGif.ResourceType(), qt.Equals, "image") assertExtWidthHeight(imageGif, ".gif", 225, 141) c.Assert(imageGif.Name(), qt.Equals, "sunset.jpg") @@ -247,7 +247,7 @@ func TestImagePermalinkPublishOrder(t *testing.T) { }() check1 := func(img images.ImageResource) { - resizedLink := "/a/sunset_hu7919355342577096259.jpg" + resizedLink := "/a/sunset_hu_3910bca82e28c9d6.jpg" c.Assert(img.RelPermalink(), qt.Equals, resizedLink) assertImageFile(c, spec.PublishFs, resizedLink, 100, 50) } @@ -288,12 +288,12 @@ func TestImageBugs(t *testing.T) { c.Assert(err, qt.IsNil) c.Assert(resized, qt.Not(qt.IsNil)) c.Assert(resized.Width(), qt.Equals, 200) - c.Assert(resized.RelPermalink(), qt.Equals, "/a/1234567890qwertyuiopasdfghjklzxcvbnm5to6eeeeee7via8eleph_hu9514381480012510326.jpg") + c.Assert(resized.RelPermalink(), qt.Equals, "/a/1234567890qwertyuiopasdfghjklzxcvbnm5to6eeeeee7via8eleph_hu_951d3980b18c52a9.jpg") resized, err = resized.Resize("100x") c.Assert(err, qt.IsNil) c.Assert(resized, qt.Not(qt.IsNil)) c.Assert(resized.Width(), qt.Equals, 100) - c.Assert(resized.RelPermalink(), qt.Equals, "/a/1234567890qwertyuiopasdfghjklzxcvbnm5to6eeeeee7via8eleph_hu1776700126481066216.jpg") + c.Assert(resized.RelPermalink(), qt.Equals, "/a/1234567890qwertyuiopasdfghjklzxcvbnm5to6eeeeee7via8eleph_hu_1daa203572ecd6ec.jpg") }) // Issue #6137 @@ -391,7 +391,7 @@ func TestImageResize8BitPNG(t *testing.T) { resized, err := image.Resize("800x") c.Assert(err, qt.IsNil) c.Assert(resized.MediaType().Type, qt.Equals, "image/png") - c.Assert(resized.RelPermalink(), qt.Equals, "/a/gohugoio_hu8582372628235034388.png") + c.Assert(resized.RelPermalink(), qt.Equals, "/a/gohugoio_hu_fe2b762e9cac406c.png") c.Assert(resized.Width(), qt.Equals, 800) } diff --git a/resources/images/config.go b/resources/images/config.go index 9655e9a51ce..6fcd2e334ba 100644 --- a/resources/images/config.go +++ b/resources/images/config.go @@ -20,6 +20,7 @@ import ( "strconv" "strings" + "github.com/gohugoio/hugo/common/hashing" "github.com/gohugoio/hugo/common/maps" "github.com/gohugoio/hugo/config" "github.com/gohugoio/hugo/media" @@ -37,6 +38,13 @@ const ( ActionFill = "fill" ) +var Actions = map[string]bool{ + ActionResize: true, + ActionCrop: true, + ActionFit: true, + ActionFill: true, +} + var ( imageFormats = map[string]Format{ ".jpg": JPEG, @@ -64,9 +72,9 @@ var ( // Add or increment if changes to an image format's processing requires // re-generation. imageFormatsVersions = map[Format]int{ - PNG: 3, // Fix transparency issue with 32 bit images. - WEBP: 2, // Fix transparency issue with 32 bit images. - GIF: 1, // Fix resize issue with animated GIFs when target != GIF. + PNG: 0, + WEBP: 0, + GIF: 0, } // Increment to mark all processed images as stale. Only use when absolutely needed. @@ -84,6 +92,7 @@ var anchorPositions = map[string]gift.Anchor{ strings.ToLower("BottomLeft"): gift.BottomLeftAnchor, strings.ToLower("Bottom"): gift.BottomAnchor, strings.ToLower("BottomRight"): gift.BottomRightAnchor, + smartCropIdentifier: SmartCropAnchor, } // These encoding hints are currently only relevant for Webp. @@ -176,7 +185,7 @@ func DecodeConfig(in map[string]any) (*config.ConfigNamespace[ImagingConfig, Ima return i, nil, err } - if i.Imaging.Anchor != "" && i.Imaging.Anchor != smartCropIdentifier { + if i.Imaging.Anchor != "" { anchor, found := anchorPositions[i.Imaging.Anchor] if !found { return i, nil, fmt.Errorf("invalid anchor value %q in imaging config", i.Anchor) @@ -201,36 +210,34 @@ func DecodeConfig(in map[string]any) (*config.ConfigNamespace[ImagingConfig, Ima return ns, nil } -func DecodeImageConfig(action string, options []string, defaults *config.ConfigNamespace[ImagingConfig, ImagingConfigInternal], sourceFormat Format) (ImageConfig, error) { +func DecodeImageConfig(options []string, defaults *config.ConfigNamespace[ImagingConfig, ImagingConfigInternal], sourceFormat Format) (ImageConfig, error) { var ( - c ImageConfig = GetDefaultImageConfig(action, defaults) + c ImageConfig = GetDefaultImageConfig(defaults) err error ) - action = strings.ToLower(action) - - c.Action = action - - if options == nil { - return c, errors.New("image options cannot be empty") + // Make to lower case, trim space and remove any empty strings. + n := 0 + for _, s := range options { + s = strings.TrimSpace(s) + if s != "" { + options[n] = strings.ToLower(s) + n++ + } } + options = options[:n] for _, part := range options { - part = strings.ToLower(part) - - if part == smartCropIdentifier { - c.AnchorStr = smartCropIdentifier + if _, ok := Actions[part]; ok { + c.Action = part } else if pos, ok := anchorPositions[part]; ok { c.Anchor = pos - c.AnchorStr = part } 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 = hexStringToColorGo(c.BgColorStr) + c.BgColor, err = hexStringToColorGo(part[1:]) if err != nil { return c, err } @@ -291,8 +298,7 @@ func DecodeImageConfig(action string, options []string, defaults *config.ConfigN } } - if action != "" && c.FilterStr == "" { - c.FilterStr = defaults.Config.Imaging.ResampleFilter + if c.Action != "" && c.Filter == nil { c.Filter = defaults.Config.ResampleFilter } @@ -300,8 +306,7 @@ func DecodeImageConfig(action string, options []string, defaults *config.ConfigN c.Hint = webpoptions.EncodingPresetPhoto } - if action != "" && c.AnchorStr == "" { - c.AnchorStr = defaults.Config.Imaging.Anchor + if c.Action != "" && c.Anchor == -1 { c.Anchor = defaults.Config.Anchor } @@ -318,10 +323,23 @@ func DecodeImageConfig(action string, options []string, defaults *config.ConfigN if c.BgColor == nil && c.TargetFormat != sourceFormat { if sourceFormat.SupportsTransparency() && !c.TargetFormat.SupportsTransparency() { c.BgColor = defaults.Config.BgColor - c.BgColorStr = defaults.Config.Imaging.BgColor } } + if mainImageVersionNumber > 0 { + options = append(options, strconv.Itoa(mainImageVersionNumber)) + } + + if v, ok := imageFormatsVersions[sourceFormat]; ok && v > 0 { + options = append(options, strconv.Itoa(v)) + } + + if smartCropVersionNumber > 0 && c.Anchor == SmartCropAnchor { + options = append(options, strconv.Itoa(smartCropVersionNumber)) + } + + c.Key = hashing.HashStringHex(options) + return c, nil } @@ -350,8 +368,7 @@ type ImageConfig struct { // not support transparency. // When set per image operation, it's used even for formats that does support // transparency. - BgColor color.Color - BgColorStr string + BgColor color.Color // Hint about what type of picture this is. Used to optimize encoding // when target is set to webp. @@ -360,57 +377,15 @@ type ImageConfig struct { Width int Height int - Filter gift.Resampling - FilterStr string + Filter gift.Resampling - Anchor gift.Anchor - AnchorStr string + Anchor gift.Anchor } -func (i ImageConfig) GetKey(format Format) string { - if i.Key != "" { - return i.Action + "_" + i.Key - } - - k := strconv.Itoa(i.Width) + "x" + strconv.Itoa(i.Height) - if i.Action != "" { - k += "_" + i.Action - } - // This slightly odd construct is here to preserve the old image keys. - if i.qualitySetForImage || i.TargetFormat.RequiresDefaultQuality() { - k += "_q" + strconv.Itoa(i.Quality) - } - if i.Rotate != 0 { - k += "_r" + strconv.Itoa(i.Rotate) - } - if i.BgColorStr != "" { - 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) - } - - k += "_" + i.FilterStr - - if i.Action == ActionFill || i.Action == ActionCrop { - k += "_" + anchor - } - - if v, ok := imageFormatsVersions[format]; ok { - k += "_" + strconv.Itoa(v) - } - - if mainImageVersionNumber > 0 { - k += "_" + strconv.Itoa(mainImageVersionNumber) - } - - return k +func (cfg ImageConfig) Reanchor(a gift.Anchor) ImageConfig { + cfg.Anchor = a + cfg.Key = hashing.HashStringHex(cfg.Key, "reanchor", a) + return cfg } type ImagingConfigInternal struct { @@ -429,7 +404,7 @@ func (i *ImagingConfigInternal) Compile(externalCfg *ImagingConfig) error { return err } - if externalCfg.Anchor != "" && externalCfg.Anchor != smartCropIdentifier { + if externalCfg.Anchor != "" { anchor, found := anchorPositions[externalCfg.Anchor] if !found { return fmt.Errorf("invalid anchor value %q in imaging config", i.Anchor) diff --git a/resources/images/config_test.go b/resources/images/config_test.go index 6dd545f2cfe..d3c9827bd55 100644 --- a/resources/images/config_test.go +++ b/resources/images/config_test.go @@ -19,6 +19,7 @@ import ( "testing" qt "github.com/frankban/quicktest" + "github.com/gohugoio/hugo/common/hashing" ) func TestDecodeConfig(t *testing.T) { @@ -106,7 +107,8 @@ func TestDecodeImageConfig(t *testing.T) { if err != nil { t.Fatal(err) } - result, err := DecodeImageConfig(this.action, strings.Fields(this.in), cfg, PNG) + options := append([]string{this.action}, strings.Fields(this.in)...) + result, err := DecodeImageConfig(options, cfg, PNG) if b, ok := this.expect.(bool); ok && !b { if err == nil { t.Errorf("[%d] parseImageConfig didn't return an expected error", i) @@ -115,15 +117,19 @@ func TestDecodeImageConfig(t *testing.T) { if err != nil { t.Fatalf("[%d] err: %s", i, err) } - if fmt.Sprint(result) != fmt.Sprint(this.expect) { - t.Fatalf("[%d] got\n%v\n but expected\n%v", i, result, this.expect) + expect := this.expect.(ImageConfig) + expect.Key = hashing.HashStringHex(options) + + if fmt.Sprint(result) != fmt.Sprint(expect) { + t.Fatalf("[%d] got\n%v\n but expected\n%v", i, result, expect) } } } } func newImageConfig(action string, width, height, quality, rotate int, filter, anchor, bgColor string) ImageConfig { - var c ImageConfig = GetDefaultImageConfig(action, nil) + var c ImageConfig = GetDefaultImageConfig(nil) + c.Action = action c.TargetFormat = PNG c.Hint = 2 c.Width = width @@ -131,26 +137,20 @@ func newImageConfig(action string, width, height, quality, rotate int, filter, a c.Quality = quality c.qualitySetForImage = quality != 75 c.Rotate = rotate - c.BgColorStr = bgColor c.BgColor, _ = hexStringToColorGo(bgColor) + c.Anchor = SmartCropAnchor if filter != "" { filter = strings.ToLower(filter) if v, ok := imageFilters[filter]; ok { c.Filter = v - c.FilterStr = filter } } if anchor != "" { - if anchor == smartCropIdentifier { - c.AnchorStr = anchor - } else { - anchor = strings.ToLower(anchor) - if v, ok := anchorPositions[anchor]; ok { - c.Anchor = v - c.AnchorStr = anchor - } + anchor = strings.ToLower(anchor) + if v, ok := anchorPositions[anchor]; ok { + c.Anchor = v } } diff --git a/resources/images/filters.go b/resources/images/filters.go index 64776affee5..9c2b9b46f9e 100644 --- a/resources/images/filters.go +++ b/resources/images/filters.go @@ -36,10 +36,11 @@ type Filters struct{} // Process creates a filter that processes an image using the given specification. func (*Filters) Process(spec any) gift.Filter { + specs := strings.ToLower(cast.ToString(spec)) return filter{ - Options: newFilterOpts(spec), + Options: newFilterOpts(specs), Filter: processFilter{ - spec: cast.ToString(spec), + spec: specs, }, } } diff --git a/resources/images/image.go b/resources/images/image.go index 4d5285acdd2..c891b016844 100644 --- a/resources/images/image.go +++ b/resources/images/image.go @@ -217,7 +217,7 @@ func (p *ImageProcessor) FiltersFromConfig(src image.Image, conf ImageConfig) ([ case "resize": filters = append(filters, gift.Resize(conf.Width, conf.Height, conf.Filter)) case "crop": - if conf.AnchorStr == smartCropIdentifier { + if conf.Anchor == SmartCropAnchor { bounds, err := p.smartCrop(src, conf.Width, conf.Height, conf.Filter) if err != nil { return nil, err @@ -232,7 +232,7 @@ func (p *ImageProcessor) FiltersFromConfig(src image.Image, conf ImageConfig) ([ filters = append(filters, gift.CropToSize(conf.Width, conf.Height, conf.Anchor)) } case "fill": - if conf.AnchorStr == smartCropIdentifier { + if conf.Anchor == SmartCropAnchor { bounds, err := p.smartCrop(src, conf.Width, conf.Height, conf.Filter) if err != nil { return nil, err @@ -329,12 +329,12 @@ func (p *ImageProcessor) doFilter(src image.Image, targetFormat Format, filters return dst, nil } -func GetDefaultImageConfig(action string, defaults *config.ConfigNamespace[ImagingConfig, ImagingConfigInternal]) ImageConfig { +func GetDefaultImageConfig(defaults *config.ConfigNamespace[ImagingConfig, ImagingConfigInternal]) ImageConfig { if defaults == nil { defaults = defaultImageConfig } return ImageConfig{ - Action: action, + Anchor: -1, // The real values start at 0. Hint: defaults.Config.Hint, Quality: defaults.Config.Imaging.Quality, } diff --git a/resources/images/images_golden_integration_test.go b/resources/images/images_golden_integration_test.go index 526e3ba534c..c49de7bd191 100644 --- a/resources/images/images_golden_integration_test.go +++ b/resources/images/images_golden_integration_test.go @@ -15,6 +15,7 @@ package images_test import ( _ "image/jpeg" + "strings" "testing" "github.com/gohugoio/hugo/resources/images/imagetesting" @@ -158,6 +159,76 @@ the last entry will win. imagetesting.RunGolden(opts) } +// Issue 13272, 13273. +func TestImagesGoldenFiltersMaskCacheIssues(t *testing.T) { + if imagetesting.SkipGoldenTests { + t.Skip("Skip golden test on this architecture") + } + + // Will be used as the base folder for generated images. + name := "filters/mask2" + + files := ` +-- hugo.toml -- +[caches] + [caches.images] + dir = ':cacheDir/golden_images' + maxAge = "30s" +[imaging] + bgColor = '#33ff44' + hint = 'photo' + quality = 75 + resampleFilter = 'Lanczos' +-- assets/sunset.jpg -- +sourcefilename: ../testdata/sunset.jpg +-- assets/mask.png -- +sourcefilename: ../testdata/mask.png + +-- layouts/index.html -- +Home. +{{ $sunset := resources.Get "sunset.jpg" }} +{{ $mask := resources.Get "mask.png" }} + + +{{ template "mask" (dict "name" "green.jpg" "base" $sunset "mask" $mask) }} + +{{ define "mask"}} +{{ $ext := path.Ext .name }} +{{ if lt (len (path.Ext .name)) 4 }} + {{ errorf "No extension in %q" .name }} +{{ end }} +{{ $format := strings.TrimPrefix "." $ext }} +{{ $spec := .spec | default (printf "resize x300 %s" $format) }} +{{ $filters := slice (images.Process $spec) (images.Mask .mask) }} +{{ $name := printf "images/%s" .name }} +{{ $img := .base.Filter $filters }} +{{ with $img | resources.Copy $name }} +{{ .Publish }} +{{ end }} +{{ end }} +` + + tempDir := t.TempDir() + + opts := imagetesting.DefaultGoldenOpts + opts.WorkingDir = tempDir + opts.T = t + opts.Name = name + opts.Files = files + opts.SkipAssertions = true + + imagetesting.RunGolden(opts) + + files = strings.Replace(files, "#33ff44", "#a83269", -1) + files = strings.Replace(files, "green", "pink", -1) + files = strings.Replace(files, "mask.png", "mask2.png", -1) + opts.Files = files + opts.SkipAssertions = false + opts.Rebuild = true + + imagetesting.RunGolden(opts) +} + func TestImagesGoldenFiltersText(t *testing.T) { t.Parallel() @@ -263,3 +334,74 @@ Home. imagetesting.RunGolden(opts) } + +func TestImagesGoldenMethods(t *testing.T) { + t.Parallel() + + if imagetesting.SkipGoldenTests { + t.Skip("Skip golden test on this architecture") + } + + // Will be used as the base folder for generated images. + name := "methods" + + files := ` +-- hugo.toml -- +[imaging] + bgColor = '#ebcc34' + hint = 'photo' + quality = 75 + resampleFilter = 'MitchellNetravali' +-- assets/sunset.jpg -- +sourcefilename: ../testdata/sunset.jpg +-- assets/gopher.png -- +sourcefilename: ../testdata/gopher-hero8.png + +-- layouts/index.html -- +Home. +{{ $sunset := resources.Get "sunset.jpg" }} +{{ $gopher := resources.Get "gopher.png" }} + + +{{ template "invoke" (dict "copyFormat" "jpg" "base" $sunset "method" "resize" "spec" "300x" ) }} +{{ template "invoke" (dict "copyFormat" "jpg" "base" $sunset "method" "resize" "spec" "x200" ) }} +{{ template "invoke" (dict "copyFormat" "jpg" "base" $sunset "method" "fill" "spec" "90x120 left" ) }} +{{ template "invoke" (dict "copyFormat" "jpg" "base" $sunset "method" "fill" "spec" "90x120 right" ) }} +{{ template "invoke" (dict "copyFormat" "jpg" "base" $sunset "method" "fit" "spec" "200x200" ) }} +{{ template "invoke" (dict "copyFormat" "jpg" "base" $sunset "method" "crop" "spec" "200x200" ) }} +{{ template "invoke" (dict "copyFormat" "jpg" "base" $sunset "method" "crop" "spec" "350x400 center" ) }} + {{ template "invoke" (dict "copyFormat" "jpg" "base" $sunset "method" "crop" "spec" "350x400 smart" ) }} +{{ template "invoke" (dict "copyFormat" "jpg" "base" $sunset "method" "crop" "spec" "350x400 center r90" ) }} +{{ template "invoke" (dict "copyFormat" "jpg" "base" $sunset "method" "crop" "spec" "350x400 center q20" ) }} +{{ template "invoke" (dict "copyFormat" "png" "base" $gopher "method" "resize" "spec" "100x" ) }} +{{ template "invoke" (dict "copyFormat" "png" "base" $gopher "method" "resize" "spec" "100x #fc03ec" ) }} +{{ template "invoke" (dict "copyFormat" "jpg" "base" $gopher "method" "resize" "spec" "100x #03fc56 jpg" ) }} + +{{ define "invoke"}} +{{ $spec := .spec }} +{{ $name := printf "images/%s-%s-%s.%s" .method ((trim .base.Name "/") | lower | anchorize) ($spec | anchorize) .copyFormat }} +{{ $img := ""}} +{{ if eq .method "resize" }} + {{ $img = .base.Resize $spec }} +{{ else if eq .method "fill" }} + {{ $img = .base.Fill $spec }} +{{ else if eq .method "fit" }} + {{ $img = .base.Fit $spec }} +{{ else if eq .method "crop" }} + {{ $img = .base.Crop $spec }} +{{ else }} + {{ errorf "Unknown method %q" .method }} +{{ end }} +{{ with $img | resources.Copy $name }} +{{ .Publish }} +{{ end }} +{{ end }} +` + + opts := imagetesting.DefaultGoldenOpts + opts.T = t + opts.Name = name + opts.Files = files + + imagetesting.RunGolden(opts) +} diff --git a/resources/images/imagetesting/testing.go b/resources/images/imagetesting/testing.go index 8e00751e0c2..22a2317a100 100644 --- a/resources/images/imagetesting/testing.go +++ b/resources/images/imagetesting/testing.go @@ -63,8 +63,18 @@ type GoldenImageTestOpts struct { // Set to true to write golden files to disk. WriteFiles bool + // If not set, a temporary directory will be created. + WorkingDir string + // Set to true to skip any assertions. Useful when adding new golden variants to a test. DevMode bool + + // Set to skip any assertions. + SkipAssertions bool + + // Whether this represents a rebuild of the same site. + // Setting this to true will keep the previous golden image set. + Rebuild bool } // To rebuild all Golden image tests, toggle WriteFiles=true and run: @@ -78,7 +88,10 @@ var DefaultGoldenOpts = GoldenImageTestOpts{ func RunGolden(opts GoldenImageTestOpts) *hugolib.IntegrationTestBuilder { opts.T.Helper() - c := hugolib.Test(opts.T, opts.Files, hugolib.TestOptWithOSFs()) // hugolib.TestOptWithPrintAndKeepTempDir(true)) + c := hugolib.Test(opts.T, opts.Files, hugolib.TestOptWithConfig(func(conf *hugolib.IntegrationTestConfig) { + conf.NeedsOsFS = true + conf.WorkingDir = opts.WorkingDir + })) c.AssertFileContent("public/index.html", "Home.") outputDir := filepath.Join(c.H.Conf.WorkingDir(), "public", "images") @@ -86,12 +99,18 @@ func RunGolden(opts GoldenImageTestOpts) *hugolib.IntegrationTestBuilder { goldenDir := filepath.Join(goldenBaseDir, filepath.FromSlash(opts.Name)) if opts.WriteFiles { c.Assert(htesting.IsRealCI(), qt.IsFalse) - c.Assert(os.MkdirAll(goldenBaseDir, 0o777), qt.IsNil) - c.Assert(os.RemoveAll(goldenDir), qt.IsNil) + if !opts.Rebuild { + c.Assert(os.MkdirAll(goldenBaseDir, 0o777), qt.IsNil) + c.Assert(os.RemoveAll(goldenDir), qt.IsNil) + } c.Assert(hugio.CopyDir(hugofs.Os, outputDir, goldenDir, nil), qt.IsNil) return c } + if opts.SkipAssertions { + return c + } + if opts.DevMode { c.Assert(htesting.IsRealCI(), qt.IsFalse) return c diff --git a/resources/images/smartcrop.go b/resources/images/smartcrop.go index 864c6de0ae0..af45c241c18 100644 --- a/resources/images/smartcrop.go +++ b/resources/images/smartcrop.go @@ -25,10 +25,10 @@ import ( const ( // Do not change. smartCropIdentifier = "smart" - - // This is just a increment, starting on 1. If Smart Crop improves its cropping, we + SmartCropAnchor = 1000 + // This is just a increment, starting on 0. If Smart Crop improves its cropping, we // need a way to trigger a re-generation of the crops in the wild, so increment this. - smartCropVersionNumber = 1 + smartCropVersionNumber = 0 ) func (p *ImageProcessor) newSmartCropAnalyzer(filter gift.Resampling) smartcrop.Analyzer { diff --git a/resources/images/testdata/images_golden/filters/mask2/green.jpg b/resources/images/testdata/images_golden/filters/mask2/green.jpg new file mode 100644 index 0000000000000000000000000000000000000000..48a9dd0830f7437a20f7acc07c45e8ed2fc8c7c9 GIT binary patch literal 8811 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 R3@w50UuVew>-ERX{{nqf%KZQU literal 0 HcmV?d00001 diff --git a/resources/images/testdata/images_golden/filters/mask2/pink.jpg b/resources/images/testdata/images_golden/filters/mask2/pink.jpg new file mode 100644 index 0000000000000000000000000000000000000000..640e41ab156598b15259c5fd999e745376b69f9f GIT binary patch literal 6147 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 literal 0 HcmV?d00001 diff --git a/resources/images/testdata/images_golden/methods/crop-sunsetjpg-200x200.jpg b/resources/images/testdata/images_golden/methods/crop-sunsetjpg-200x200.jpg new file mode 100644 index 0000000000000000000000000000000000000000..10569ab2c55facee47a0efb5f33fa88717694953 GIT binary patch literal 2524 zcmbV~dsq`^7RDzDArPR73|FHFh$Iz)A<=;4qE&(z1v7vbjIzX)C3FJ>3L=7vh_yjP zB^WPQAR%&5*IFaRwu%B-Aq#2{ya0k+1i{rHVz?Eu-vsKj``_-)^UQoR=bZ1n@9%u0 zd#-DTjzUI;h6F>r5rIH3HZ~%f!e*u>CZ@IvEat(B?CcjWvLlg}ETy_EaddSek;qJn z>k1l!!LWDn^6~WWrqUT67-M5&Qxj7gGcy|x2aTRZ1vE@aA=jFqdHo?ffht@mdIYzW*K5*oI3 z+x8vdd;YvP>Mzm!n8a_BlJ}+TPt819glA+PZss`(F1Cycrz&F!FKqlXh%;ViE)4W^urC z7U5)UV8W5UyQV$g+mf)j5G6#U>S=_|Sb6SIjUqT*JmMR_pqu&sy|tfZ2$-ki!3<-%zF+Ky@* zh*q*I^8^^4k3vvR3lu@QFt0>uQ{=i$No`A}tdYmt;XR4yi!cYMKbI@NI^2Qu6yR-2 zu)Gr?hviC$4u;T!ii@}*H`qXv#ug=0g#P!UD8eA6jr0{0=3NqTIq^R>azqCyG&Dg& zczH0_5Ony!B3D`72*Qeq+|Z8X`NS=hHKiE;ni`%2u99Q3vySyh;C~`L`8Bm`yE4fCSYE6)={NRDN6lg2S+bhQ!8Yl4y{d0K?Yb7o|s*MGJ;Y`yLsNg0vS|T3iT#A z30IkLk>0kI1zdz=1c96x00n6(i0}&VRC3k!*jbV}Y1G5HIS<3NlFiPQ)Q#?xga2p%c$}4bpqd2_@JyH+ z`a@DC4FH!YNxgjwMYJHP#B+i;E?2r)=6a+j6 ztj(x*gT+x21sd*9UkECiAaFJ{*_LZFU)x{U^d!;WJ(^Sk-&#DhtCd?fk}Gct_(hUL zjU35sa-%0vhrq;>&=>#U6%zsdC!Xx%R3ZJ##RsFCK(IRmjCVrDXd1mZm^e?I$B2@3 zv4*(!Bhgq3h-H1xr(AsVl3n2HD~cMT>(^L*%>Ftz+ajcSz`U@!QVBKD_ z)!3A}y0cC%Lysk~JevrpZs?#!vV5-0yi;c~Ms zwZvBRY>ZFcoGmU)^0YhbjT_i4SV3}hTCIu zYu`^){L*iXrw7x(HkGy0(*ml`5Z7cwi^bHF-zHVoi$}YKnk}!QPnQoIP!Ak<8eP~~ z;4)So@8)mk7#^|eW?koKxBDF`XUandUEJtg;TRsD*D0?HI{z&@ctM!Cvv(lUvo0eg z5V_K7dFFCJxzIywB5GZ2cR6S4o|RM=Z#ADXT0a!5DwbXP!Cl)xh|)oKH`@y&>T0LW zu?pr(OKsuSjDhszA{|t@k#?%q$2WwnZ8-3q{jkNl>G7BV%{Y>>ecszvLz5Tj3uh)6 z(vgVw)zc$5R3{_Dw2o7I@9Chk35U82#k(KrAn!i3ddI<* zeP2p$^7} zvWKf5xJ(bnteyFFxVZWny9RNK3X}ZFPF#Bx*)v^>n6bFHz9t=%W*0eC`gSW}@>W^O zYF0M=THnefrUtH^fnxkych2nBm+%M2LqG*MyS)2|`xNW?-mydXUH?`QFw90f5u5*|`^^Dj7CU(40K_!q(EbASK< literal 0 HcmV?d00001 diff --git a/resources/images/testdata/images_golden/methods/crop-sunsetjpg-350x400-center-q20.jpg b/resources/images/testdata/images_golden/methods/crop-sunsetjpg-350x400-center-q20.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1b316ff17c5fb6e86117cf4fad7b6172aa309adb GIT binary patch literal 4026 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

#CDXQY8cz}vHMGb2KIekcctl*-V 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 TGG147_`JCc<$f6A$BX{~pt#FN literal 0 HcmV?d00001 diff --git a/resources/images/testdata/images_golden/methods/crop-sunsetjpg-350x400-center.jpg b/resources/images/testdata/images_golden/methods/crop-sunsetjpg-350x400-center.jpg new file mode 100644 index 0000000000000000000000000000000000000000..aa89eedf28d53e0b8318fe131f28d1b0e78945f9 GIT binary patch literal 8619 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 R-by(aGM3`xXYu>(e*tIwFC=!wcXrfG{zE zo4^ooI2?&YpiC{yOwnjlYn(a8!p_FQ-pcCNF#>s-PDCtF+M61N2&o>VH;!Iidr zDS4SYg-Qk?kw{at>1;Ez*$91VjM9SP%pYGBkn&004r4z6SU^ zATR_9Gl3(JDD*4<41z$wPzVeLg&JRG8Seln7KU^5^fNIJi-!}~7UZ0ga|kE@x`%j1 ztJayq+?|U=Sz67uwwdSRN^~Pp7cE}0l;*W^)sF#z^q}DIb?YM{qc%h*Y)Ra@ZF>@H zPfF_EwDf%$hYlaf%P;tqbNoap_heal#VNu0N?}#?1(Epjm8Qwji_VvGct9dstIQn61{G)DS^3xOufPCW^&u^fAc(6tf7z%|z;a@x; zaGJ3oSSZZV(*)-i29IZ(6UaFT3;&XHbq|qF6owYh+}(<@bf)&q8~sB1D*AVz-2Wx| z4*Kpfr~y+5$oOFpEZ_@_0#{#i=SHUoD{?(bN9}@f!l-v(kR6>M>hR(wRKeo$bb^FH zO6Qbw1f=AA^~C~FFA1n}6n@AOYYWw7Ejqp_z$FOjB8D{8$th|uu~0H)PJKUV)4+eRVRyxVBJT6*ah}xG)FRG|X7ANg1E;WtqF>eCZ}7p=mh6IB?^2 zf*$6DJYK(2nRm8dAC_F#5Wr2s74+&P?1rrQ_9>f*iejmA%4ClZH__3j^sd0c%_cCs z@_>@$pG9g|kww%M&=8istcnDk1P!lWbIBoiQ!A!~dy+E1kRDJb#_2?s<^!wM{Qkt$ z;uZd*!r9fP)N+LLRnXgm&fQQr|( z+p6XB$;Cf>gy5sRnl?UpxLOuY0X~2;$+))y{hQ&fzz_(;yow-|VOC`bZ_&NbW`*0LqipHME(+;#qtvrL zaM#5!wLF{5?kHa6vp3~YqdyK6yXKXqTZ*yr@DF$!-%1~fEv-=94xtYPzjBMUlV&Jx zd1MN>Nf#^HS@y)AC5w-2$ooio#P3hQhX*JL?d~1veQFe*`NP3y!Hr!@#Na(4-etM+ z+Gtj(MJ(fw5NX%VTG7kuB0?O!bW|l01r{E0 zQ$lyy{TN9XZw{OkvbLUnFPZiOKf^`}(JY(6lQh3jnm^W@DBUqpHcS5D5>01v#XiVx zg66F6XzMS+hx$h6_P&fUyKdH&O@ZX7d2h`gL36sRGUG7T17XWx8KGA8wupNxixJinb2Pt7KtwPm}+Azjsi{kCJz zZ{XeZ_q8_p?L%w)cS7UN$!jv&*FcH@@1oNR&Gbgfr1>Zx(e3gnji`A?!eU)>9#0HJ lZqql1){bUv2g!g)n&w{8nb4WZ&*EKLJW?+oTH#>m{0n9);E4bL literal 0 HcmV?d00001 diff --git a/resources/images/testdata/images_golden/methods/fill-sunsetjpg-90x120-right.jpg b/resources/images/testdata/images_golden/methods/fill-sunsetjpg-90x120-right.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4afcb6decdb5ecf1040dc037000d7e09ab4db81d GIT binary patch literal 1631 zcmbW!dpOg390%~&+GXrpD6o*{Qw$; z0#Qgb2!a?4S{+NkVKp?cL~R{S!XlC(c@fFLz{t$j!pOwN)WCq^XlY|drO{}H7IdbQ zy|b+Y%^rrqV6YlkJseKY-q^s{{y#@~6TqVZ0T2v_nE+~d7#t5%w!lgN0EUEp4e)nh zYH$P+1)?$P8d`uF3=UUAz>!D)Rrc#tXDt*X{a_WRrcxe^OK#fBT@HEwaxT6roT5vi)?HMr#0 zsRmxX{0ugrL0%~wlGV%lWyP_nd{hLDEbXw6iDjDn?0fRFsA&nrsV4XA2onXU7xJEz zTuAn7A@`(&iI6wM29gM;5QX3M4k&o#knFUolD@Z<-5@V7Naa8NW@5K5pb7_){Bdzm z$k8oU3mXVmG9qv_pQ>1Oy=@YRok*4@Adhmh%7UtH-R4v;YlCtd>LN9Cy)Gtm(=*HP zY$zvrIT!EOe4bx#Qug{?RzTw-y{fEEHfKxy&m|hZOBcjxckpzhM7 zs+Y702#vadP|UQYhpceqlA!mfi)bwP=2;_uYYP8@sf{%~8i(oHzso4h>gq_vDevgI z=thmFr3$x8qZvo)7a8lKhPmU-ZcoppG_hW4{vpm{xC``OY&C7|1g+(J=1Irj_YN`! zmyY#JVoc_L$!E{Z_*fCP_{~3ll(KT)lwc^dx~VYlIwKU~)jJ1YQZBv=$f2 z2i{~E2xs%$!gZG1F)3sO#980Ie&F!3mR%q5fA)T6beHm)CI)7U-ikY8e#UPn24SL( zocA>}Y!7T7;ppK5e(g8X1Br9BNmp#iv8z}R$QrT!40e)=T5x7{)d{ZD8S{h_WLt3~ zXLc|oT?y3YkKs&z%uDZhbPeYz(QbMo>n^4i8{6i*KwVsy|UW~8sbquJQI@|_uJg$Zbx?^*1Jj`ZiQKPBdje&kzmm@yQ5 zWjJwavV0Ap!*r`U=<+r`ReFDp(4u($mUiDNrGThsvqBgVe1zMhsF+GUy?St6?@5BN xFMU-+R1J|KtQ=&K+>PclP{%b3ChpLiGGi3d1Blufjp)3*G__3+%?W5_-(PM2$5a3S literal 0 HcmV?d00001 diff --git a/resources/images/testdata/images_golden/methods/fit-sunsetjpg-200x200.jpg b/resources/images/testdata/images_golden/methods/fit-sunsetjpg-200x200.jpg new file mode 100644 index 0000000000000000000000000000000000000000..75c6a30b02467bf9cd1ac0ba39ad4ba3464c269c GIT binary patch literal 2877 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 literal 0 HcmV?d00001 diff --git a/resources/images/testdata/images_golden/methods/resize-gopherpng-100x-03fc56-jpg.jpg b/resources/images/testdata/images_golden/methods/resize-gopherpng-100x-03fc56-jpg.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2e78ce461c194c7a1528b7a434bb4509d51a079a GIT binary patch literal 3181 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 literal 0 HcmV?d00001 diff --git a/resources/images/testdata/images_golden/methods/resize-gopherpng-100x.png b/resources/images/testdata/images_golden/methods/resize-gopherpng-100x.png new file mode 100644 index 0000000000000000000000000000000000000000..9bca47b147f8ce0d40528fd99a1c3ce5e7db5a90 GIT binary patch literal 3528 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 literal 0 HcmV?d00001 diff --git a/resources/images/testdata/images_golden/methods/resize-sunsetjpg-300x.jpg b/resources/images/testdata/images_golden/methods/resize-sunsetjpg-300x.jpg new file mode 100644 index 0000000000000000000000000000000000000000..319385e9a5ac217e6241409636979b77fd2de69a GIT binary patch literal 4950 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 JsWJA#{SQ