diff --git a/server-rs/crates/api-server/src/editor_project.rs b/server-rs/crates/api-server/src/editor_project.rs index 39ec8a3c4..e13a581e6 100644 --- a/server-rs/crates/api-server/src/editor_project.rs +++ b/server-rs/crates/api-server/src/editor_project.rs @@ -1662,6 +1662,9 @@ pub(crate) async fn generate_editor_image_for_owner( None }; + // TODO the image size passed to api is already normalized, should remove those normalize + // and let resize here to get the proper size + image = restore_editor_generated_image_output_dimensions(image, image_size.as_ref())?; let (width, height) = image::load_from_memory(image.bytes.as_slice()) .map(|image| (image.width(), image.height())) .unwrap_or((1024, 1024)); @@ -2219,6 +2222,54 @@ fn restore_editor_image_edit_output_dimensions( extension: "png".to_string(), }) } +fn restore_editor_generated_image_output_dimensions( + output: DownloadedOpenAiImage, + // TODO primitive obsession + target_size: &str, +) -> Result { + let (target_width, target_height) = target_size.split_once('x').ok_or_else(|| { + AppError::from_status(StatusCode::BAD_GATEWAY).with_details(json!({ + "provider": "vector-engine", + "message": "尺寸无效", + })) + })?; + let target_width = target_width.parse::().map_err(|_| { + AppError::from_status(StatusCode::BAD_GATEWAY).with_details(json!({ + "provider": "vector-engine", + "message": "宽度无效", + })) + })?; + let target_height = target_height.parse::().map_err(|_| { + AppError::from_status(StatusCode::BAD_GATEWAY).with_details(json!({ + "provider": "vector-engine", + "message": "高度无效", + })) + })?; + let decoded = image::load_from_memory(output.bytes.as_slice()).map_err(|error| { + AppError::from_status(StatusCode::BAD_GATEWAY).with_details(json!({ + "provider": "vector-engine", + "message": format!("素材生成结果不是有效图片:{error}"), + })) + })?; + if decoded.width() == target_width && decoded.height() == target_height { + return Ok(output); + } + + let restored = decoded.resize_to_fill( + target_width, + target_height, + image::imageops::FilterType::Lanczos3, + ); + Ok(DownloadedOpenAiImage { + bytes: encode_editor_image_edit_png( + restored, + StatusCode::BAD_GATEWAY, + "恢复宣发素材交付尺寸失败", + )?, + mime_type: "image/png".to_string(), + extension: "png".to_string(), + }) +} pub async fn edit_editor_image( State(state): State, @@ -7497,6 +7548,30 @@ mod tests { assert_eq!(restored.extension, "png"); } + #[test] + fn publication_material_generation_restores_provider_output_to_workflow_dimensions() { + let image = image::DynamicImage::new_rgba8(944, 704); + let mut bytes = Cursor::new(Vec::new()); + image + .write_to(&mut bytes, image::ImageFormat::Png) + .expect("test image should encode"); + + let restored = restore_editor_generated_image_output_dimensions( + DownloadedOpenAiImage { + bytes: bytes.into_inner(), + mime_type: "image/png".to_string(), + extension: "png".to_string(), + }, + "720x540", + ) + .expect("provider output should restore delivery dimensions"); + let restored_image = image::load_from_memory(restored.bytes.as_slice()).unwrap(); + + assert_eq!((restored_image.width(), restored_image.height()), (720, 540)); + assert_eq!(restored.mime_type, "image/png"); + assert_eq!(restored.extension, "png"); + } + #[test] fn editor_generation_dimensions_follow_model_options() { let default_generation = normalize_editor_generation_options(None, Some("1:1"), Some("1K"));