fix: 工程类型与素材风格标签分离,支持双栏自定义标签
- 工程层面:新增 PROJECT_TYPES(2D平台跳跃/角色扮演等8种),替代原先与素材生成重复的风格标签栏 - 素材生成层面:StyleSelector 作为素材风格主配置项,新增 CustomTagsEditor 支持任务级自定义标签 - extractTags 合并工程类型、风格分类、工程自定义标签、任务自定义标签 - StyleSelector 支持 categories 参数复用 - 后端:spritesheet 拆分失败回退为单帧、GIF 上传为独立素材、AssetResponse 增加 metadata 字段
This commit is contained in:
@@ -4,6 +4,7 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"image"
|
||||
"image/png"
|
||||
"strings"
|
||||
|
||||
@@ -159,6 +160,7 @@ var formatAdapterNode = compose.InvokableLambda(func(ctx context.Context, input
|
||||
})
|
||||
|
||||
// processSpriteSheet 将单张精灵表拆分为独立帧并生成 GIF 预览。
|
||||
// 拆分失败时回退为单帧,不阻塞管线。
|
||||
func processSpriteSheet(ctx context.Context, img GeneratedImage, params AssetParams, resolution int) (PipelineOutput, error) {
|
||||
l := logger.FromCtx(ctx)
|
||||
src, err := png.Decode(bytes.NewReader(img.Data))
|
||||
@@ -175,13 +177,19 @@ func processSpriteSheet(ctx context.Context, img GeneratedImage, params AssetPar
|
||||
|
||||
frames, err := splitsprite.Process(src, opts)
|
||||
if err != nil {
|
||||
l.Error("format_adapter split sprite sheet failed", "error", err)
|
||||
return PipelineOutput{}, fmt.Errorf("split sprite sheet: %w", err)
|
||||
l.Warn("format_adapter split sprite sheet failed, falling back to single frame", "error", err)
|
||||
// 回退:整张图作为单帧
|
||||
frames = []image.Image{src}
|
||||
}
|
||||
l.Info("format_adapter split sprite sheet", "frame_count", len(frames))
|
||||
|
||||
// 帧 → Asset
|
||||
assets := make([]Asset, 0, len(frames))
|
||||
// 帧 → Asset(先保留原始精灵表,再追加拆分后的帧)
|
||||
assets := make([]Asset, 0, len(frames)+2)
|
||||
assets = append(assets, Asset{
|
||||
Data: img.Data,
|
||||
Format: img.Format,
|
||||
URL: "output/spritesheet.png",
|
||||
})
|
||||
for i, f := range frames {
|
||||
var buf bytes.Buffer
|
||||
if err := png.Encode(&buf, f); err != nil {
|
||||
@@ -195,12 +203,19 @@ func processSpriteSheet(ctx context.Context, img GeneratedImage, params AssetPar
|
||||
})
|
||||
}
|
||||
|
||||
// GIF 预览
|
||||
// GIF 预览 → Asset(排在帧之后)
|
||||
var gifURL string
|
||||
var gifBuf bytes.Buffer
|
||||
if err := gifmaker.Encode(&gifBuf, frames, nil); err != nil {
|
||||
l.Warn("format_adapter generate GIF preview failed", "error", err)
|
||||
} else {
|
||||
l.Info("format_adapter generated GIF preview", "size_bytes", gifBuf.Len())
|
||||
gifURL = "output/preview.gif"
|
||||
assets = append(assets, Asset{
|
||||
Data: gifBuf.Bytes(),
|
||||
Format: "gif",
|
||||
URL: gifURL,
|
||||
})
|
||||
}
|
||||
|
||||
fw, fh := 0, 0
|
||||
@@ -216,7 +231,7 @@ func processSpriteSheet(ctx context.Context, img GeneratedImage, params AssetPar
|
||||
FrameHeight: fh,
|
||||
FrameCount: len(frames),
|
||||
Directions: params.Frames.Directions,
|
||||
GIFPreview: gifBuf.Bytes(),
|
||||
GIFURL: gifURL,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
@@ -234,7 +249,7 @@ func buildStyleDescription(projectStyle, taskStyle map[string]string) string {
|
||||
return "风格约束:" + strings.Join(parts, ";")
|
||||
}
|
||||
|
||||
// appendTechNotes 在无标签(不走 PromptAgent)时补上技术参数段。
|
||||
// appendTechNotes 在提示词末尾追加技术参数段。
|
||||
func appendTechNotes(prompt, assetType string, params AssetParams) string {
|
||||
var parts []string
|
||||
if prompt != "" {
|
||||
@@ -250,7 +265,9 @@ func appendTechNotes(prompt, assetType string, params AssetParams) string {
|
||||
if params.Frames.FramesPerDirection > 0 {
|
||||
parts = append(parts, fmt.Sprintf("每方向帧数: %d", params.Frames.FramesPerDirection))
|
||||
}
|
||||
if params.Format != "" {
|
||||
if params.Format == "spritesheet" {
|
||||
parts = append(parts, "输出格式: spritesheet(将所有帧排列在一张图上,帧之间用8-16像素纯白间隙分隔,等间距网格布局)")
|
||||
} else if params.Format != "" {
|
||||
parts = append(parts, fmt.Sprintf("输出格式: %s", params.Format))
|
||||
}
|
||||
return strings.Join(parts, ";")
|
||||
|
||||
@@ -64,9 +64,9 @@ type Asset struct {
|
||||
|
||||
// AssetMetadata 素材元数据
|
||||
type AssetMetadata struct {
|
||||
FrameWidth int
|
||||
FrameHeight int
|
||||
FrameCount int
|
||||
Directions int
|
||||
GIFPreview []byte `json:"-"` // animated GIF preview (not serialized)
|
||||
FrameWidth int `json:"frameWidth"`
|
||||
FrameHeight int `json:"frameHeight"`
|
||||
FrameCount int `json:"frameCount"`
|
||||
Directions int `json:"directions"`
|
||||
GIFURL string `json:"gifUrl,omitempty"` // animated GIF preview URL
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user