mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-01-04 11:19:39 -06:00
Pre allocating slices with a target size reduces the number of allocations because we already know how big the slice will be and therefore need to allocate only once
111 lines
3.0 KiB
Go
111 lines
3.0 KiB
Go
package thumbnail
|
|
|
|
import (
|
|
"fmt"
|
|
"image"
|
|
"math"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
const (
|
|
_resolutionSeperator = "x"
|
|
)
|
|
|
|
// ParseResolution returns an image.Rectangle representing the resolution given as a string
|
|
func ParseResolution(s string) (image.Rectangle, error) {
|
|
parts := strings.Split(s, _resolutionSeperator)
|
|
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 postive 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 mapRatio(given image.Rectangle, other image.Rectangle) image.Rectangle {
|
|
isLandscape := given.Dx() > given.Dy()
|
|
ratio := float64(given.Dx()) / float64(given.Dy())
|
|
if isLandscape {
|
|
return image.Rect(0, 0, other.Dx(), int(float64(other.Dx())/ratio))
|
|
}
|
|
return image.Rect(0, 0, int(float64(other.Dy())*ratio), other.Dy())
|
|
}
|
|
|
|
func dimensionLength(rect image.Rectangle, isLandscape bool) int {
|
|
if isLandscape {
|
|
return rect.Dx()
|
|
}
|
|
return rect.Dy()
|
|
}
|