You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
197 lines
4.0 KiB
197 lines
4.0 KiB
package server
|
|
|
|
import (
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
var imageAspectRatioSeparators = strings.NewReplacer(
|
|
":", ":",
|
|
"/", ":",
|
|
"/", ":",
|
|
"×", ":",
|
|
"*", ":",
|
|
"x", ":",
|
|
"X", ":",
|
|
)
|
|
|
|
var canonicalImageAspectRatioLabels = map[string]string{
|
|
"7:3": "21:9",
|
|
"16:9": "16:9",
|
|
"4:3": "4:3",
|
|
"3:2": "3:2",
|
|
"1:1": "1:1",
|
|
"9:16": "9:16",
|
|
"3:4": "3:4",
|
|
"2:3": "2:3",
|
|
"5:4": "5:4",
|
|
"4:5": "4:5",
|
|
}
|
|
|
|
var seedreamAspectRatioSizes = map[string]string{
|
|
"21:9": "2520x1080",
|
|
"16:9": "2048x1152",
|
|
"4:3": "2048x1536",
|
|
"3:2": "2304x1536",
|
|
"1:1": "2048x2048",
|
|
"9:16": "1152x2048",
|
|
"3:4": "1536x2048",
|
|
"2:3": "1536x2304",
|
|
"5:4": "1920x1536",
|
|
"4:5": "1536x1920",
|
|
}
|
|
|
|
var exactImageSizeAspectRatios = map[string]string{
|
|
"1024x1024": "1:1",
|
|
"1920x1920": "1:1",
|
|
"1536x1024": "3:2",
|
|
"1024x1536": "2:3",
|
|
"1792x1024": "16:9",
|
|
"1024x1792": "9:16",
|
|
}
|
|
|
|
func normalizeImageAspectRatio(value string) string {
|
|
width, height, ok := parseAspectRatioDimensions(value)
|
|
if !ok {
|
|
return strings.TrimSpace(value)
|
|
}
|
|
return canonicalAspectRatioLabel(width, height)
|
|
}
|
|
|
|
func parseAspectRatioDimensions(value string) (int, int, bool) {
|
|
value = strings.TrimSpace(value)
|
|
if value == "" {
|
|
return 0, 0, false
|
|
}
|
|
|
|
normalized := imageAspectRatioSeparators.Replace(value)
|
|
parts := strings.Split(normalized, ":")
|
|
if len(parts) != 2 {
|
|
return 0, 0, false
|
|
}
|
|
|
|
width, errWidth := strconv.Atoi(strings.TrimSpace(parts[0]))
|
|
height, errHeight := strconv.Atoi(strings.TrimSpace(parts[1]))
|
|
if errWidth != nil || errHeight != nil || width <= 0 || height <= 0 {
|
|
return 0, 0, false
|
|
}
|
|
return width, height, true
|
|
}
|
|
|
|
func canonicalAspectRatioLabel(width, height int) string {
|
|
rw, rh := reduceImageRatio(width, height)
|
|
key := fmt.Sprintf("%d:%d", rw, rh)
|
|
if label, ok := canonicalImageAspectRatioLabels[key]; ok {
|
|
return label
|
|
}
|
|
return key
|
|
}
|
|
|
|
func reduceImageRatio(width, height int) (int, int) {
|
|
g := imageGCD(width, height)
|
|
if g == 0 {
|
|
return width, height
|
|
}
|
|
return width / g, height / g
|
|
}
|
|
|
|
func imageGCD(a, b int) int {
|
|
for b != 0 {
|
|
a, b = b, a%b
|
|
}
|
|
if a < 0 {
|
|
return -a
|
|
}
|
|
return a
|
|
}
|
|
|
|
func parseExactImageSize(value string) (int, int, bool) {
|
|
value = strings.TrimSpace(strings.ToLower(value))
|
|
if value == "" {
|
|
return 0, 0, false
|
|
}
|
|
parts := strings.Split(value, "x")
|
|
if len(parts) != 2 {
|
|
return 0, 0, false
|
|
}
|
|
width, errWidth := strconv.Atoi(strings.TrimSpace(parts[0]))
|
|
height, errHeight := strconv.Atoi(strings.TrimSpace(parts[1]))
|
|
if errWidth != nil || errHeight != nil || width <= 0 || height <= 0 {
|
|
return 0, 0, false
|
|
}
|
|
return width, height, true
|
|
}
|
|
|
|
func normalizeImageSizeToken(value string) string {
|
|
value = strings.TrimSpace(value)
|
|
if value == "" {
|
|
return ""
|
|
}
|
|
switch strings.ToUpper(value) {
|
|
case "1K", "2K", "4K":
|
|
return strings.ToUpper(value)
|
|
}
|
|
width, height, ok := parseExactImageSize(value)
|
|
if !ok {
|
|
return value
|
|
}
|
|
return fmt.Sprintf("%dx%d", width, height)
|
|
}
|
|
|
|
func deriveAspectRatioFromImageSize(value string) string {
|
|
value = normalizeImageSizeToken(value)
|
|
if ratio, ok := exactImageSizeAspectRatios[value]; ok {
|
|
return ratio
|
|
}
|
|
width, height, ok := parseExactImageSize(value)
|
|
if !ok {
|
|
if ratio := normalizeImageAspectRatio(value); ratio != "" {
|
|
return ratio
|
|
}
|
|
return ""
|
|
}
|
|
return canonicalAspectRatioLabel(width, height)
|
|
}
|
|
|
|
func geminiImageSizeFromValue(value string) string {
|
|
value = normalizeImageSizeToken(value)
|
|
switch value {
|
|
case "1K", "2K", "4K":
|
|
return value
|
|
}
|
|
width, height, ok := parseExactImageSize(value)
|
|
if !ok {
|
|
return ""
|
|
}
|
|
longEdge := width
|
|
if height > longEdge {
|
|
longEdge = height
|
|
}
|
|
switch {
|
|
case longEdge <= 1024:
|
|
return "1K"
|
|
case longEdge <= 2560:
|
|
return "2K"
|
|
default:
|
|
return "4K"
|
|
}
|
|
}
|
|
|
|
func seedreamImageSizeFromAspectRatio(value string) string {
|
|
if ratio := normalizeImageAspectRatio(value); ratio != "" {
|
|
if size, ok := seedreamAspectRatioSizes[ratio]; ok {
|
|
return size
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func supportedSeedreamSize(modelID, value string) bool {
|
|
value = strings.ToUpper(strings.TrimSpace(value))
|
|
if value == "" {
|
|
return false
|
|
}
|
|
_, ok := supportedSeedreamSizes(modelID)[value]
|
|
return ok
|
|
}
|
|
|