合并 master 并完整恢复多产物原图回填

恢复角色、图标和 UI 成功结果中的透明主结果与 provider 原图双产物回填

恢复图标和 UI 切片在 provider 原图右侧的排列,并保持透明主结果为 generatedLayerId 锚点

保留 OSS image_url、短时签名、缓冲释放、逐帧重签名及 object key 降级约束

保留尺寸一致不回读、尺寸漂移时短时回读原图回贴 Alpha 的所有权策略

同步权威文档、决策记录和已知陷阱,并验证画布定向测试、格式与编码检查
This commit is contained in:
2026-07-18 11:44:26 +00:00
5 changed files with 47 additions and 23 deletions
@@ -4685,6 +4685,7 @@ pub(crate) async fn generate_editor_icon_spritesheet_for_owner(
build_icon_spritesheet_canvas_layer_items(
completion,
spritesheet_record.resource.as_ref(),
source_record.resource.as_ref(),
icon_image_srcs.as_slice(),
)
} else {
@@ -5431,6 +5432,7 @@ pub(crate) async fn extract_editor_ui_design_assets_for_owner(
build_icon_spritesheet_canvas_layer_items(
completion,
spritesheet_record.resource.as_ref(),
source_record.resource.as_ref(),
icon_image_srcs.as_slice(),
)
} else {
@@ -6811,11 +6813,12 @@ fn build_multi_asset_canvas_layer_items(
fn build_icon_spritesheet_canvas_layer_items(
completion: &EditorCanvasGenerationCompletionRequest,
spritesheet_resource: Option<&EditorProjectResourcePayload>,
source_resource: Option<&EditorProjectResourcePayload>,
icons: &[EditorIconSpritesheetIconResponse],
) -> (Vec<Value>, Option<String>) {
let placeholder = completion.placeholder.clone();
let (mut items, primary_layer_id) =
build_primary_canvas_layer_items(completion, spritesheet_resource);
build_multi_asset_canvas_layer_items(completion, spritesheet_resource, source_resource);
if items.is_empty() {
return (items, primary_layer_id);
}
@@ -9820,7 +9823,7 @@ mod tests {
}
#[test]
fn editor_canvas_spritesheet_items_include_processed_sheet_and_slices() {
fn editor_canvas_spritesheet_items_include_processed_sheet_provider_source_and_slices() {
let completion = EditorCanvasGenerationCompletionRequest {
dialog_id: Some("dialog-ui-assets".to_string()),
title: "UI素材图集".to_string(),
@@ -9839,6 +9842,12 @@ mod tests {
512,
512,
);
let source = editor_project_resource_for_canvas_test(
"resource-ui-sheet-source",
"icon-spritesheet",
512,
512,
);
let slice = editor_project_resource_for_canvas_test("resource-ui-slice", "icon", 128, 128);
let icons = vec![EditorIconSpritesheetIconResponse {
name: "开始按钮".to_string(),
@@ -9852,22 +9861,25 @@ mod tests {
let (items, primary_layer_id) = build_icon_spritesheet_canvas_layer_items(
&completion,
Some(&processed),
Some(&source),
icons.as_slice(),
);
assert_eq!(items.len(), 2);
assert_eq!(items.len(), 3);
assert_eq!(
primary_layer_id.as_deref(),
Some("layer-editor-resource-resource-ui-sheet")
);
assert_eq!(items[0]["resourceId"], json!("resource-ui-sheet"));
assert_eq!(items[1]["resourceId"], json!("resource-ui-slice"));
let sheet_right = items[0]["x"].as_f64().unwrap() + items[0]["width"].as_f64().unwrap();
assert!(items[1]["x"].as_f64().unwrap() > sheet_right);
assert_eq!(items[1]["resourceId"], json!("resource-ui-sheet-source"));
assert_eq!(items[1]["title"], json!("UI素材图集(原图)"));
assert_eq!(items[2]["resourceId"], json!("resource-ui-slice"));
let source_right = items[1]["x"].as_f64().unwrap() + items[1]["width"].as_f64().unwrap();
assert!(items[2]["x"].as_f64().unwrap() > source_right);
}
#[test]
fn editor_canvas_spritesheet_items_keep_only_processed_sheet_when_slices_are_empty() {
fn editor_canvas_spritesheet_items_keep_processed_sheet_and_provider_source_without_slices() {
let completion = EditorCanvasGenerationCompletionRequest {
dialog_id: Some("dialog-icon-sheet".to_string()),
title: "图标素材图集".to_string(),
@@ -9886,11 +9898,23 @@ mod tests {
512,
512,
);
let (items, primary_layer_id) =
build_icon_spritesheet_canvas_layer_items(&completion, Some(&processed), &[]);
let source = editor_project_resource_for_canvas_test(
"resource-icon-sheet-source",
"icon-spritesheet",
512,
512,
);
let (items, primary_layer_id) = build_icon_spritesheet_canvas_layer_items(
&completion,
Some(&processed),
Some(&source),
&[],
);
assert_eq!(items.len(), 1);
assert_eq!(items.len(), 2);
assert_eq!(items[0]["resourceId"], json!("resource-icon-sheet"));
assert_eq!(items[1]["resourceId"], json!("resource-icon-sheet-source"));
assert_eq!(items[1]["title"], json!("图标素材图集(原图)"));
assert_eq!(
primary_layer_id.as_deref(),
Some("layer-editor-resource-resource-icon-sheet")