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 }