From c743ca146ef1737c19ee3f67f0c47a0a22e0982a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=BE=B7=E5=AE=87?= Date: Fri, 11 Sep 2026 19:53:14 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A4=8D=E7=94=A8=E5=9B=BE=E5=83=8F=E4=B8=8B?= =?UTF-8?q?=E8=BD=BD=E7=9A=84=E5=85=B1=E4=BA=AB=E5=AD=97=E8=8A=82=E7=BC=93?= =?UTF-8?q?=E5=86=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 将 DownloadedImage.bytes 改为 bytes::Bytes。 让远程下载和图标切片下游避免不必要的完整复制。 同步更新图像处理实现与测试 fixture。 --- .../crates/api-server/src/editor_project.rs | 2 +- .../api-server/src/editor_project_icon.rs | 6 ++-- .../src/generated_asset_sheets/sheet.rs | 28 +++++++++---------- .../platform-image/src/pixel_art_snapper.rs | 6 ++-- .../src/vector_engine/image_source.rs | 6 ++-- .../platform-image/src/vector_engine/types.rs | 3 +- .../tests/generated_asset_sheets.rs | 2 +- 7 files changed, 27 insertions(+), 26 deletions(-) diff --git a/server-rs/crates/api-server/src/editor_project.rs b/server-rs/crates/api-server/src/editor_project.rs index eb63aa79e..c6529863a 100644 --- a/server-rs/crates/api-server/src/editor_project.rs +++ b/server-rs/crates/api-server/src/editor_project.rs @@ -19064,7 +19064,7 @@ mod tests { #[tokio::test] async fn icon_spritesheet_expired_deadline_skips_blocking_image_work() { let source = DownloadedImage { - bytes: b"not-an-image".to_vec(), + bytes: bytes::Bytes::from_static(b"not-an-image"), mime_type: "image/png".to_string(), extension: "png".to_string(), }; diff --git a/server-rs/crates/api-server/src/editor_project_icon.rs b/server-rs/crates/api-server/src/editor_project_icon.rs index a065f626b..39deb0bd7 100644 --- a/server-rs/crates/api-server/src/editor_project_icon.rs +++ b/server-rs/crates/api-server/src/editor_project_icon.rs @@ -2324,7 +2324,7 @@ pub async fn split_editor_icon_spritesheet( Err(_) => return Err(editor_icon_spritesheet_processing_timeout_error()), }; let source = DownloadedImage { - bytes: reference.bytes.to_vec(), + bytes: reference.bytes, mime_type: reference.mime_type, extension: "png".to_string(), }; @@ -2653,7 +2653,7 @@ pub(crate) fn editor_icon_spritesheet_warning_after_persist_error( } fn validate_editor_icon_spritesheet_source(source: &DownloadedImage) -> Result<(), AppError> { - let reader = image::ImageReader::new(Cursor::new(source.bytes.as_slice())) + let reader = image::ImageReader::new(Cursor::new(source.bytes.as_ref())) .with_guessed_format() .map_err(|error| { AppError::from_status(StatusCode::UNPROCESSABLE_ENTITY).with_details(json!({ @@ -2931,7 +2931,7 @@ mod tests { .write_to(&mut std::io::Cursor::new(&mut bytes), ImageFormat::Png) .expect("fixture png should encode"); let source = DownloadedImage { - bytes, + bytes: bytes.into(), mime_type: "image/png".to_string(), extension: "png".to_string(), }; diff --git a/server-rs/crates/platform-image/src/generated_asset_sheets/sheet.rs b/server-rs/crates/platform-image/src/generated_asset_sheets/sheet.rs index b0d7ec81b..496bb9824 100644 --- a/server-rs/crates/platform-image/src/generated_asset_sheets/sheet.rs +++ b/server-rs/crates/platform-image/src/generated_asset_sheets/sheet.rs @@ -34,7 +34,7 @@ pub fn slice_generated_asset_sheet( let grid_size_u32 = u32::try_from(grid_size).map_err(|_| { GeneratedAssetSheetError::invalid_request("系列素材图集的 n 超出可支持范围。") })?; - let source = image::load_from_memory(image.bytes.as_slice()).map_err(|error| { + let source = image::load_from_memory(image.bytes.as_ref()).map_err(|error| { GeneratedAssetSheetError::decode_image(format!("系列素材图集解码失败:{error}")) })?; let source = apply_generated_asset_sheet_green_screen_alpha(source); @@ -94,7 +94,7 @@ pub fn slice_generated_asset_sheet_two_items_per_row( let grid_size_u32 = u32::try_from(grid_size).map_err(|_| { GeneratedAssetSheetError::invalid_request("系列素材图集的 n 超出可支持范围。") })?; - let source = image::load_from_memory(image.bytes.as_slice()).map_err(|error| { + let source = image::load_from_memory(image.bytes.as_ref()).map_err(|error| { GeneratedAssetSheetError::decode_image(format!("系列素材图集解码失败:{error}")) })?; let source = apply_generated_asset_sheet_green_screen_alpha(source); @@ -243,7 +243,7 @@ pub fn prepare_generated_icon_spritesheet_all_by_connected_components( "图标 spritesheet 累计裁剪像素上限必须大于 0。", )); } - let source = image::load_from_memory(image.bytes.as_slice()).map_err(|error| { + let source = image::load_from_memory(image.bytes.as_ref()).map_err(|error| { GeneratedAssetSheetError::decode_image(format!("图标 spritesheet 解码失败:{error}")) })?; let source = apply_generated_asset_sheet_green_screen_alpha(source); @@ -263,7 +263,7 @@ pub fn prepare_generated_icon_spritesheet_all_by_connected_components( pub fn prepare_generated_icon_spritesheet_grid_2x2( image: &crate::DownloadedImage, ) -> Result { - let source = image::load_from_memory(image.bytes.as_slice()).map_err(|error| { + let source = image::load_from_memory(image.bytes.as_ref()).map_err(|error| { GeneratedAssetSheetError::decode_image(format!("图标 spritesheet 解码失败:{error}")) })?; let source = apply_generated_asset_sheet_green_screen_alpha(source).into_rgba8(); @@ -942,7 +942,7 @@ mod tests { } } crate::DownloadedImage { - bytes: encode_png(sheet), + bytes: encode_png(sheet).into(), mime_type: "image/png".to_string(), extension: "png".to_string(), } @@ -1031,7 +1031,7 @@ mod tests { sheet.put_pixel(x0 + 40, y0 + 8, Rgba(color)); } let source = crate::DownloadedImage { - bytes: encode_png(sheet), + bytes: encode_png(sheet).into(), mime_type: "image/png".to_string(), extension: "png".to_string(), }; @@ -1063,7 +1063,7 @@ mod tests { } let source = crate::DownloadedImage { - bytes: encode_png(sheet), + bytes: encode_png(sheet).into(), mime_type: "image/png".to_string(), extension: "png".to_string(), }; @@ -1095,7 +1095,7 @@ mod tests { } let source = crate::DownloadedImage { - bytes: encode_png(sheet), + bytes: encode_png(sheet).into(), mime_type: "image/png".to_string(), extension: "png".to_string(), }; @@ -1137,7 +1137,7 @@ mod tests { } let source = crate::DownloadedImage { - bytes: encode_png(sheet), + bytes: encode_png(sheet).into(), mime_type: "image/png".to_string(), extension: "png".to_string(), }; @@ -1165,7 +1165,7 @@ mod tests { } let source = crate::DownloadedImage { - bytes: encode_png(sheet), + bytes: encode_png(sheet).into(), mime_type: "image/png".to_string(), extension: "png".to_string(), }; @@ -1206,7 +1206,7 @@ mod tests { } let source = crate::DownloadedImage { - bytes: encode_png(sheet), + bytes: encode_png(sheet).into(), mime_type: "image/png".to_string(), extension: "png".to_string(), }; @@ -1240,7 +1240,7 @@ mod tests { } let source = crate::DownloadedImage { - bytes: encode_png(sheet), + bytes: encode_png(sheet).into(), mime_type: "image/png".to_string(), extension: "png".to_string(), }; @@ -1272,7 +1272,7 @@ mod tests { } let source = crate::DownloadedImage { - bytes: encode_png(sheet), + bytes: encode_png(sheet).into(), mime_type: "image/png".to_string(), extension: "png".to_string(), }; @@ -1303,7 +1303,7 @@ mod tests { } let source = crate::DownloadedImage { - bytes: encode_png(sheet), + bytes: encode_png(sheet).into(), mime_type: "image/png".to_string(), extension: "png".to_string(), }; diff --git a/server-rs/crates/platform-image/src/pixel_art_snapper.rs b/server-rs/crates/platform-image/src/pixel_art_snapper.rs index 589a26a6f..9f7ac5c33 100644 --- a/server-rs/crates/platform-image/src/pixel_art_snapper.rs +++ b/server-rs/crates/platform-image/src/pixel_art_snapper.rs @@ -266,7 +266,7 @@ fn decode_rgba_source( return Err(PixelArtSnapError::InvalidInput(format!("{input} 为空"))); } - let dimension_reader = image::ImageReader::new(Cursor::new(source.bytes.as_slice())) + let dimension_reader = image::ImageReader::new(Cursor::new(source.bytes.as_ref())) .with_guessed_format() .map_err(|error| PixelArtSnapError::Decode { input, @@ -281,7 +281,7 @@ fn decode_rgba_source( })?; validate_dimensions(width, height, input)?; - let mut reader = image::ImageReader::new(Cursor::new(source.bytes.as_slice())) + let mut reader = image::ImageReader::new(Cursor::new(source.bytes.as_ref())) .with_guessed_format() .map_err(|error| PixelArtSnapError::Decode { input, @@ -465,7 +465,7 @@ fn encode_png(image: RgbaImage) -> Result { .write_to(&mut cursor, ImageFormat::Png) .map_err(|error| PixelArtSnapError::Encode(error.to_string()))?; Ok(DownloadedImage { - bytes: cursor.into_inner(), + bytes: cursor.into_inner().into(), mime_type: "image/png".to_string(), extension: "png".to_string(), }) diff --git a/server-rs/crates/platform-image/src/vector_engine/image_source.rs b/server-rs/crates/platform-image/src/vector_engine/image_source.rs index 29fe42a45..7bbe073d4 100644 --- a/server-rs/crates/platform-image/src/vector_engine/image_source.rs +++ b/server-rs/crates/platform-image/src/vector_engine/image_source.rs @@ -52,7 +52,7 @@ pub async fn download_remote_image( Ok(DownloadedImage { extension: mime_to_extension(normalized_mime_type.as_str()).to_string(), mime_type: normalized_mime_type, - bytes: body.to_vec(), + bytes: body, }) } @@ -162,7 +162,7 @@ pub(crate) async fn resolve_reference_images( .await .map_err(|error| contextualize_reference_download_error(error, failure_context))?; resolved.push(ReferenceImage { - bytes: Bytes::from(downloaded.bytes), + bytes: downloaded.bytes, mime_type: downloaded.mime_type.clone(), file_name: format!( "reference-{index}.{}", @@ -248,7 +248,7 @@ pub(crate) fn decode_generated_image_base64(raw: &str) -> Option, + /// Shared immutable image bytes so callers can pass downloaded buffers downstream without copying. + pub bytes: Bytes, pub mime_type: String, pub extension: String, } diff --git a/server-rs/crates/platform-image/tests/generated_asset_sheets.rs b/server-rs/crates/platform-image/tests/generated_asset_sheets.rs index d3bc61705..b3c860528 100644 --- a/server-rs/crates/platform-image/tests/generated_asset_sheets.rs +++ b/server-rs/crates/platform-image/tests/generated_asset_sheets.rs @@ -42,7 +42,7 @@ fn build_test_sheet(width: u32, height: u32) -> DownloadedImage { } DownloadedImage { - bytes: encode_image(sheet), + bytes: encode_image(sheet).into(), mime_type: "image/png".to_string(), extension: "png".to_string(), }