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 x", 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() }