use predefined resolutions for thumbnail generation

This commit is contained in:
David Christofas
2020-03-23 11:52:33 +01:00
parent 43bf107207
commit 3de4584a45
12 changed files with 125 additions and 56 deletions
@@ -9,6 +9,20 @@ func TestParseWithEmptyString(t *testing.T) {
}
}
func TestParseWithInvalidWidth(t *testing.T) {
_, err := Parse("invalidx42")
if err == nil {
t.Error("Parse with invalid width should return an error.")
}
}
func TestParseWithInvalidHeight(t *testing.T) {
_, err := Parse("42xinvalid")
if err == nil {
t.Error("Parse with invalid height should return an error.")
}
}
func TestParse(t *testing.T) {
rStr := "42x23"
r, _ := Parse(rStr)
+28 -11
View File
@@ -3,6 +3,7 @@ package resolution
import (
"fmt"
"math"
"sort"
)
// Init creates an instance of Resolutions from resolution strings.
@@ -15,6 +16,16 @@ func Init(rStrs []string) (Resolutions, error) {
}
rs = append(rs, r)
}
sort.Slice(rs, func(i, j int) bool {
left := rs[i]
right := rs[j]
leftSize := left.Width * left.Height
rightSize := right.Width * right.Height
return leftSize < rightSize
})
return rs, nil
}
@@ -22,31 +33,37 @@ func Init(rStrs []string) (Resolutions, error) {
type Resolutions []Resolution
// ClosestMatch returns the resolution which is closest to the provided resolution.
// If there is no exact match the resolution will be the next higher one.
// If the given resolution is bigger than all available resolutions the biggest available one is used.
func (r Resolutions) ClosestMatch(width, height int) Resolution {
if len(r) == 0 {
return Resolution{Width: width, Height: height}
}
isLandscape := width > height
givenLen := math.Max(float64(width), float64(height))
givenLen := int(math.Max(float64(width), float64(height)))
// Initialize with the first resolution
match := r[0]
matchLen := dimensionLength(match, isLandscape)
minDiff := math.Abs(givenLen - float64(matchLen))
var match Resolution
minDiff := math.MaxInt32
for i := 1; i < len(r); i++ {
r := r[i]
rLen := dimensionLength(r, isLandscape)
diff := math.Abs(givenLen - float64(rLen))
if diff <= minDiff {
minDiff = diff
match = r
current := r[i]
len := dimensionLength(current, isLandscape)
diff := givenLen - len
if diff > 0 {
continue
}
absDiff := int(math.Abs(float64(diff)))
if absDiff < minDiff {
minDiff = absDiff
match = current
}
}
if match == (Resolution{}) {
match = r[len(r)-1]
}
return match
}
+23 -4
View File
@@ -52,6 +52,25 @@ func TestInitWithMultipleResolutions(t *testing.T) {
}
}
func TestInitWithMultipleResolutionsShouldBeSorted(t *testing.T) {
rStrs := []string{"32x32", "64x64", "16x16", "128x128"}
rs, err := Init(rStrs)
if err != nil {
t.Errorf("Init with valid parameter should not fail. Error: %s.\n", err.Error())
}
for i := 0; i < len(rs)-1; i++ {
current := rs[i]
currentSize := current.Width * current.Height
next := rs[i]
nextSize := next.Width * next.Height
if currentSize > nextSize {
t.Error("Resolutions are not sorted.")
}
}
}
func TestClosestMatchWithEmptyResolutions(t *testing.T) {
rs, _ := Init(nil)
width := 24
@@ -66,12 +85,12 @@ func TestClosestMatchWithEmptyResolutions(t *testing.T) {
func TestClosestMatch(t *testing.T) {
rs, _ := Init([]string{"16x16", "24x24", "32x32", "64x64", "128x128"})
table := [][]int{
[]int{17, 17, 16, 16},
[]int{12, 17, 16, 16},
[]int{17, 17, 24, 24},
[]int{12, 17, 24, 24},
[]int{24, 24, 24, 24},
[]int{20, 20, 24, 24},
[]int{20, 80, 64, 64},
[]int{80, 20, 64, 64},
[]int{20, 80, 128, 128},
[]int{80, 20, 128, 128},
[]int{48, 48, 64, 64},
[]int{1024, 1024, 128, 128},
}
+1 -2
View File
@@ -6,7 +6,6 @@ import (
"io/ioutil"
"os"
"path/filepath"
"strconv"
"github.com/owncloud/ocis-pkg/v2/log"
"github.com/owncloud/ocis-thumbnails/pkg/config"
@@ -68,7 +67,7 @@ func (s FileSystem) Set(key string, img []byte) error {
func (s FileSystem) BuildKey(ctx Context) string {
etag := ctx.ETag
filetype := ctx.Types[0]
filename := strconv.Itoa(ctx.Width) + "x" + strconv.Itoa(ctx.Height) + "." + filetype
filename := ctx.Resolution.String() + "." + filetype
key := new(bytes.Buffer)
key.WriteString(etag[:2])
+1 -1
View File
@@ -32,7 +32,7 @@ func (s InMemory) Set(key string, thumbnail []byte) error {
func (s InMemory) BuildKey(ctx Context) string {
parts := []string{
ctx.ETag,
string(ctx.Width) + "x" + string(ctx.Height),
ctx.Resolution.String(),
strings.Join(ctx.Types, ","),
}
return strings.Join(parts, "+")
+5 -4
View File
@@ -1,11 +1,12 @@
package storage
import "github.com/owncloud/ocis-thumbnails/pkg/thumbnails/resolution"
// Context combines different attributes needed for storage operations.
type Context struct {
ETag string
Types []string
Width int
Height int
ETag string
Types []string
Resolution resolution.Resolution
}
// Storage defines the interface for a thumbnail store.
+9 -10
View File
@@ -6,16 +6,16 @@ import (
"github.com/nfnt/resize"
"github.com/owncloud/ocis-pkg/v2/log"
"github.com/owncloud/ocis-thumbnails/pkg/thumbnails/resolution"
"github.com/owncloud/ocis-thumbnails/pkg/thumbnails/storage"
)
// Context bundles information needed to generate a thumbnail for afile
type Context struct {
Width int
Height int
ImagePath string
Encoder Encoder
ETag string
Resolution resolution.Resolution
ImagePath string
Encoder Encoder
ETag string
}
// Manager is responsible for generating thumbnails
@@ -69,16 +69,15 @@ func (s SimpleManager) GetStored(ctx Context) []byte {
}
func (s SimpleManager) generate(ctx Context, img image.Image) image.Image {
thumbnail := resize.Thumbnail(uint(ctx.Width), uint(ctx.Height), img, resize.Lanczos2)
thumbnail := resize.Thumbnail(uint(ctx.Resolution.Width), uint(ctx.Resolution.Height), img, resize.Lanczos2)
return thumbnail
}
func mapToStorageContext(ctx Context) storage.Context {
sCtx := storage.Context{
ETag: ctx.ETag,
Width: ctx.Width,
Height: ctx.Height,
Types: ctx.Encoder.Types(),
ETag: ctx.ETag,
Resolution: ctx.Resolution,
Types: ctx.Encoder.Types(),
}
return sCtx
}