From fc2f4b71ce6f75501c8d79f94e8edb75ff6b6b35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=BE=B7=E5=AE=87?= Date: Sat, 12 Sep 2026 10:38:00 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=88=86=E7=A6=BB=E6=B5=81?= =?UTF-8?q?=E7=A8=8B=E8=BD=AC=E6=8D=A2=E9=9D=9E=20PNG=20=E6=BA=90=E5=9B=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 在上传图片编辑请求前将 JPEG 和 WebP 转码为 PNG 新增 JPEG 转 PNG 单元测试并将转码放入阻塞任务 --- .../commands/separation/workflow/extract.rs | 40 +++++++++++++++++-- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/apps/ai-game-creator-shell/src-tauri/src/ui_editor/commands/separation/workflow/extract.rs b/apps/ai-game-creator-shell/src-tauri/src/ui_editor/commands/separation/workflow/extract.rs index a5f08e433..08d8cffc4 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/ui_editor/commands/separation/workflow/extract.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/ui_editor/commands/separation/workflow/extract.rs @@ -54,15 +54,20 @@ async fn raw_extract_inner( .strip_prefix("data:") .and_then(|value| value.strip_suffix(";base64")) .unwrap_or("image/png"); - if !mime.eq_ignore_ascii_case("image/png") { - return Err("图片分离请求只支持 PNG 源图".to_string()); - } + let is_png = mime.eq_ignore_ascii_case("image/png"); let image_bytes = base64::engine::general_purpose::STANDARD .decode(data.trim()) .map_err(|error| format!("解码源图失败:{error}"))?; if image_bytes.is_empty() { return Err("源图不能为空".to_string()); } + let image_bytes = if is_png { + image_bytes + } else { + tokio::task::spawn_blocking(move || normalize_source_image_to_png(image_bytes)) + .await + .map_err(|error| format!("转换源图任务失败:{error}"))?? + }; let client = crate::http_client::agc_main_site_client_builder() .build() .map_err(|error| format!("创建图片编辑客户端失败:{error}"))?; @@ -122,6 +127,35 @@ async fn raw_extract_inner( result } +fn normalize_source_image_to_png(image_bytes: Vec) -> Result, String> { + let image = image::load_from_memory(&image_bytes) + .map_err(|error| format!("解码非 PNG 源图失败:{error}"))?; + let mut png_bytes = Vec::new(); + image + .write_to(&mut Cursor::new(&mut png_bytes), image::ImageFormat::Png) + .map_err(|error| format!("将源图转换为 PNG 失败:{error}"))?; + Ok(png_bytes) +} + +#[cfg(test)] +mod tests { + use super::normalize_source_image_to_png; + use image::{DynamicImage, ImageFormat, Rgb, RgbImage}; + use std::io::Cursor; + + #[test] + fn converts_jpeg_source_to_png() { + let image = DynamicImage::ImageRgb8(RgbImage::from_pixel(2, 2, Rgb([255, 0, 0]))); + let mut jpeg = Vec::new(); + image + .write_to(&mut Cursor::new(&mut jpeg), ImageFormat::Jpeg) + .expect("encode jpeg"); + + let png = normalize_source_image_to_png(jpeg).expect("convert jpeg"); + assert_eq!(&png[..8], b"\x89PNG\r\n\x1a\n"); + } +} + pub(super) async fn write_processed_image( processed_url: String, target: PathBuf,