Files
opencloud/services/thumbnails/pkg/thumbnail/resolutions.go
Christian Richter 78064e6bab rename folder extensions -> services
Signed-off-by: Christian Richter <crichter@owncloud.com>
2022-06-27 14:05:36 +02:00

102 lines
2.7 KiB
Go

package thumbnail
import (
"fmt"
"image"
"math"
"strconv"
"strings"
"github.com/pkg/errors"
)
const (
_resolutionSeparator = "x"
)
// ParseResolution returns an image.Rectangle representing the resolution given as a string
func ParseResolution(s string) (image.Rectangle, error) {
parts := strings.Split(s, _resolutionSeparator)
if len(parts) != 2 {
return image.Rectangle{}, fmt.Errorf("failed to parse resolution: %s. Expected format <width>x<height>", s)
}
width, err := strconv.Atoi(parts[0])
if err != nil {
return image.Rectangle{}, fmt.Errorf("width: %s has an invalid value. Expected an integer", parts[0])
}
height, err := strconv.Atoi(parts[1])
if err != nil {
return image.Rectangle{}, fmt.Errorf("height: %s has an invalid value. Expected an integer", parts[1])
}
return image.Rect(0, 0, width, height), nil
}
// Resolutions is a list of image.Rectangle representing resolutions.
type Resolutions []image.Rectangle
// ParseResolutions creates an instance of Resolutions from resolution strings.
func ParseResolutions(strs []string) (Resolutions, error) {
rs := make(Resolutions, 0, len(strs))
for _, s := range strs {
r, err := ParseResolution(s)
if err != nil {
return nil, errors.Wrap(err, "could not parse resolutions")
}
rs = append(rs, r)
}
return rs, nil
}
// 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 (rs Resolutions) ClosestMatch(requested image.Rectangle, sourceSize image.Rectangle) image.Rectangle {
isLandscape := sourceSize.Dx() > sourceSize.Dy()
sourceLen := dimensionLength(sourceSize, isLandscape)
requestedLen := dimensionLength(requested, isLandscape)
isSourceSmaller := sourceLen < requestedLen
// We don't want to scale images up.
if isSourceSmaller {
return sourceSize
}
if len(rs) == 0 {
return requested
}
var match image.Rectangle
// Since we want to search for the smallest difference we start with the highest possible number
minDiff := math.MaxInt32
for _, current := range rs {
cLen := dimensionLength(current, isLandscape)
diff := requestedLen - cLen
if diff > 0 {
// current is smaller
continue
}
// Convert diff to positive value
// Multiplying by -1 is safe since we aren't getting positive numbers here
// because of the check above
absDiff := diff * -1
if absDiff < minDiff {
minDiff = absDiff
match = current
}
}
if (match == image.Rectangle{}) {
match = rs[len(rs)-1]
}
return match
}
func dimensionLength(rect image.Rectangle, isLandscape bool) int {
if isLandscape {
return rect.Dx()
}
return rect.Dy()
}