diff --git a/backend/internal/service/provider.go b/backend/internal/service/provider.go index 6d4d44d80..cea374cd3 100644 --- a/backend/internal/service/provider.go +++ b/backend/internal/service/provider.go @@ -833,7 +833,10 @@ func grokImageInputURL(media providerMedia) (string, error) { return openAIImageInputURL(media) } -const volcengineArkImageMaxPixels = 4624220 +const ( + volcengineArkImageMinPixels = 3686400 + volcengineArkImageMaxPixels = 4624220 +) func runVolcengineArkImageTask(ctx context.Context, input canvasGenerationInput) (map[string]interface{}, error) { if input.Mask != nil { @@ -896,12 +899,26 @@ func normalizeVolcengineArkImageSize(value string) string { if widthErr != nil || heightErr != nil || width <= 0 || height <= 0 { return size } - if int64(width)*int64(height) <= volcengineArkImageMaxPixels { + pixels := int64(width) * int64(height) + if pixels >= volcengineArkImageMinPixels && pixels <= volcengineArkImageMaxPixels { return size } - scale := math.Sqrt(float64(volcengineArkImageMaxPixels) / (float64(width) * float64(height))) - width = int(math.Floor(float64(width)*scale/2)) * 2 - height = int(math.Floor(float64(height)*scale/2)) * 2 + targetPixels := volcengineArkImageMaxPixels + round := math.Floor + if pixels < volcengineArkImageMinPixels { + targetPixels = volcengineArkImageMinPixels + round = math.Ceil + } + scale := math.Sqrt(float64(targetPixels) / float64(pixels)) + width = int(round(float64(width)*scale/2)) * 2 + height = int(round(float64(height)*scale/2)) * 2 + for width > 2 && height > 2 && int64(width)*int64(height) < volcengineArkImageMinPixels { + if width >= height { + width += 2 + } else { + height += 2 + } + } for width > 2 && height > 2 && int64(width)*int64(height) > volcengineArkImageMaxPixels { if width >= height { width -= 2 diff --git a/backend/internal/service/provider_test.go b/backend/internal/service/provider_test.go index 89f740be2..fbc3e7bb7 100644 --- a/backend/internal/service/provider_test.go +++ b/backend/internal/service/provider_test.go @@ -142,11 +142,32 @@ func TestVolcengineArkImageBodyUsesJSONReferencesAndDownscalesSize(t *testing.T) } width, _ := strconv.Atoi(parts[0]) height, _ := strconv.Atoi(parts[1]) - if width%2 != 0 || height%2 != 0 || int64(width)*int64(height) > volcengineArkImageMaxPixels { + if width%2 != 0 || height%2 != 0 || int64(width)*int64(height) < volcengineArkImageMinPixels || int64(width)*int64(height) > volcengineArkImageMaxPixels { t.Fatalf("downscaled size = %q", size) } } +func TestVolcengineArkImageBodyUpscalesPresetBelowMinimumPixels(t *testing.T) { + body, err := volcengineArkImageBody(canvasGenerationInput{ + Prompt: "vertical image", + Config: providerConfig{Model: "doubao-seedream-test", Size: "9:16"}, + }) + if err != nil { + t.Fatalf("volcengineArkImageBody() error = %v", err) + } + size, _ := body["size"].(string) + parts := strings.Split(size, "x") + if len(parts) != 2 { + t.Fatalf("size = %q", size) + } + width, _ := strconv.Atoi(parts[0]) + height, _ := strconv.Atoi(parts[1]) + pixels := int64(width) * int64(height) + if width%2 != 0 || height%2 != 0 || pixels < volcengineArkImageMinPixels || pixels > volcengineArkImageMaxPixels { + t.Fatalf("normalized size = %q", size) + } +} + func TestVolcengineArkImageRejectsMaskBeforeRequest(t *testing.T) { _, err := runImageTask(context.Background(), canvasGenerationInput{ Prompt: "edit only the masked area",