生成风格 pixelArt 同时约束提示词
style="pixelArt" 此前只驱动 provider 回图后的确定性像素规整,完全不参与 提示词。但 snapper 是几何对齐器——先检测网格步长再按格重采样;provider 交 一张柔和渐变图时横纵两轴都测不到步长,生成路径用的 legacy profile 会退到 min(width,height)/64 统一网格兜底,产出的是马赛克而不是像素画。原语义等于 「随便生成什么,然后强行网格化」。 注入点固定在 generate_editor_image_for_owner 与 generate_editor_icon_spritesheet_for_owner 构造提交提示词的位置,包住既有 builder 的返回值,builder 签名和输出契约不变。登录态路由、外部 API v1、 异步 job worker 三个入口都汇聚到这两个函数,一处覆盖。 约束句按链路分三条,追加在末尾并独立成行:普通图片没有抠像底色,用 「画面为像素风格」;角色形象和图标图集生成后都要按纯色抠像,绿幕底必须保持 平整,分别用「角色主体为像素风格」和「每个图标素材均为像素风格」,都不出现 「画面」级别的像素化要求,否则与同一段提示词里既有的「纯色背景必须平整无 纹理、无渐变」互相拆台。 实测只提「像素风格」效果已可接受,因此不注入网格密度、色板色数和抗锯齿等 约束。source-guard 钉住注入排在 create_openai_ 之前——被挪到之后时产物看起来 仍然「成功」,只是静默退化成统一网格马赛克,普通测试抓不到。 约束句随 submitted_prompt 写入 editor_project_resource 的 prompt 列;响应体 返回的仍是用户原文,前端显示不变;不新增 OSS PUT、项目资源、素材记录或画布 图层。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -115,7 +115,7 @@ python3 .codex/skills/genarrative-external-editor-api/scripts/genarrative_extern
|
||||
|
||||
## Request Patterns
|
||||
|
||||
For image and icon generation, the request-body top-level `style` field controls deterministic post-processing and is distinct from `generationInputs.artSpec.style`, which describes visual style for prompting. Pass `style="pixelArt"` in Python or `"style": "pixelArt"` in JSON to enable pixel-art snapping on supported generation types; use `"none"` or omit the field otherwise. Verify compatibility and fallback semantics in `references/api-selection.md`.
|
||||
For image and icon generation, the request-body top-level `style` field both appends a server-side pixel-art clause to the prompt sent to the provider and enables deterministic post-processing. It is distinct from `generationInputs.artSpec.style`, which is stored metadata describing the requested visual language. Pass `style="pixelArt"` in Python or `"style": "pixelArt"` in JSON to enable both on supported generation types; use `"none"` or omit the field otherwise, in which case the submitted prompt is byte-for-byte what you sent. Verify compatibility and fallback semantics in `references/api-selection.md`.
|
||||
|
||||
For Python callers, prefer:
|
||||
|
||||
|
||||
@@ -86,10 +86,10 @@ Ask a follow-up only when two routes could both be correct and produce different
|
||||
|
||||
## Image Post-processing Style
|
||||
|
||||
The request-body top-level `style` field controls deterministic image post-processing. It is separate from `generationInputs.artSpec.style`, which only describes the requested visual language for prompting.
|
||||
The request-body top-level `style` field controls both the prompt actually submitted to the provider and deterministic image post-processing. It is separate from `generationInputs.artSpec.style`, which is stored metadata describing the requested visual language.
|
||||
|
||||
- Omitted, `null`, an empty string, and `"none"` all disable post-processing without a warning.
|
||||
- `"pixelArt"` enables deterministic pixel-art snapping for ordinary image generation (omit `kind`), `kind: "character"`, and icon spritesheet generation.
|
||||
- Omitted, `null`, an empty string, and `"none"` all disable post-processing without a warning, and leave the submitted prompt byte-for-byte as you sent it.
|
||||
- `"pixelArt"` appends one server-side clause to the end of the submitted prompt and enables deterministic pixel-art snapping, for ordinary image generation (omit `kind`), `kind: "character"`, and icon spritesheet generation. The clause is scoped per path — `画面为像素风格` for ordinary images, `角色主体为像素风格` for characters, and `每个图标素材均为像素风格` for icon spritesheets. Character and icon spritesheet generation are matted against a flat key-colour background afterwards, so their clauses deliberately never demand a whole-canvas pixelation that would fight the flat-background requirement already in those prompts.
|
||||
- Unknown strings, or `"pixelArt"` on unsupported image kinds such as `spec`, `quick-edit`, `ui-design`, or `publication-material`, continue without style processing and return `warning.code: "unsupported-image-style"`.
|
||||
- A non-string JSON value is malformed and returns HTTP `400`. Keep the field extensible; do not treat the current examples as a closed client-side enum.
|
||||
|
||||
|
||||
@@ -2563,7 +2563,7 @@
|
||||
"none",
|
||||
"pixelArt"
|
||||
],
|
||||
"description": "可选生成后处理风格,当前识别 none 与 pixelArt。省略、null、空字符串或 none 按无风格处理;pixelArt 仅支持普通图片(kind 省略)和 character。未知字符串或不支持该风格的 kind 按 none 继续生成并返回 unsupported-image-style 告警;非字符串值返回 400。"
|
||||
"description": "可选生成风格,当前识别 none 与 pixelArt。省略、null、空字符串或 none 按无风格处理,提交给 provider 的提示词与未带该字段时逐字一致;pixelArt 仅支持普通图片(kind 省略)和 character,会在提示词末尾追加一行像素风约束(普通图片为「画面为像素风格」,character 为「角色主体为像素风格」)并在回图后执行像素规整。未知字符串或不支持该风格的 kind 按 none 继续生成并返回 unsupported-image-style 告警;非字符串值返回 400。"
|
||||
},
|
||||
"size": {
|
||||
"type": "string",
|
||||
@@ -2977,7 +2977,7 @@
|
||||
"none",
|
||||
"pixelArt"
|
||||
],
|
||||
"description": "可选生成后处理风格,当前识别 none 与 pixelArt。省略、null、空字符串或 none 按无风格处理;pixelArt 启用图标图集像素规整。未知字符串按 none 继续生成并返回 unsupported-image-style 告警;非字符串值返回 400。"
|
||||
"description": "可选生成风格,当前识别 none 与 pixelArt。省略、null、空字符串或 none 按无风格处理,提交给 provider 的提示词与未带该字段时逐字一致;pixelArt 会在提示词末尾追加一行「每个图标素材均为像素风格」并启用图标图集像素规整。未知字符串按 none 继续生成并返回 unsupported-image-style 告警;非字符串值返回 400。"
|
||||
},
|
||||
"model": {
|
||||
"type": "string",
|
||||
|
||||
@@ -5911,3 +5911,14 @@
|
||||
- 决策:预览的业务失败与浏览器基础设施失败分流。基础设施失败按稳定 kind 持久化并立即失败结束当前 run;preview readiness/playtest 的 manifest 完成分别绑定当前 revision smoke 和根合同 browser receipt,read-only 文本交付不能绕过。
|
||||
- 决策:game-chat 的 client-owned Runner 在活动任务期间禁止关闭客户端;关闭前复用既有 durable idle 真相源,避免另建 UI busy 状态。用户明确暂停/取消并达到 idle 后再退出,不能靠重启后自动重放未知 Provider 结果。
|
||||
- 决策:规范 Agent reasoning 默认由角色职责分层,显式 per-Agent patch 优先;配置状态对外展示实际 timing/retry,避免全局文件、per-Agent resolver 与历史 run snapshot 混淆。
|
||||
|
||||
## 2026-08-01 生成风格 pixelArt 同时约束提示词
|
||||
|
||||
- 背景:`style="pixelArt"` 此前只驱动 provider 返回后的确定性像素规整,完全不参与提示词拼接。但 `platform-image` 的 snapper 是几何对齐器——先检测网格步长再按格重采样;provider 交一张柔和渐变图时横纵两轴都检测不到步长,生成路径使用的 legacy profile 会退到 `min(width,height)/64` 统一网格兜底,产出的是马赛克而不是像素画。也就是原语义等于「随便生成什么,然后强行网格化」。
|
||||
- 决策:`pixelArt` 从「纯后处理风格」改为「提示词约束 + 后处理」。注入点固定在 `generate_editor_image_for_owner` 与 `generate_editor_icon_spritesheet_for_owner` 各自构造 `submitted_prompt` / `prompt` 的位置,包住既有 builder 的返回值,builder 签名与其既有输出契约不变。三个入口(登录态路由、外部 API v1、异步 job worker)都汇聚到这两个函数,一处注入全覆盖。`None` 必须原样返回原提示词。
|
||||
- 作用域按链路分三条措辞,不共用同一句:普通图片没有抠像底色,用「画面为像素风格」;角色形象与图标图集生成后都要按纯色抠像,绿幕底必须保持平整,分别用「角色主体为像素风格」和「每个图标素材均为像素风格」,都不得出现「画面」级别的像素化要求,否则与同一段提示词里既有的「纯色背景必须平整无纹理、无渐变」互相拆台。角色形象的提示词已禁止出现角色以外的场景内容,因此只点名角色;图标图集一张图内是多个彼此分离的素材,需要逐个点名。
|
||||
- 强度边界:实测只提「像素风格」效果已可接受,因此不注入网格密度、色板色数、抗锯齿等约束。约束句一律追加在提示词末尾并独立成行,不前置、不改写 builder 内部语句。`kind` 为 `spec / quick-edit / ui-design / publication-material` 时 `pixel_art_supported` 已把 `pixelArt` 降级为 `None`,注入对它们不生效;「修改图片」链路的 DTO 没有 `style` 字段,完全不受影响。
|
||||
- 反向提示词:同步从画布四个生图入口(普通图片、角色形象共用一条,UI 设计图,修改图片两个 provider 分支)的 negative prompt 中移除「低清晰度」——该词按字面否定低分辨率,与以低分辨率重采样为本质的像素风直接对冲。其余玩法(拼图、消除、跳跃、方洞、大鱼、吠叫、自定义世界场景图)的同名词条不动,本次只收口画布项目。
|
||||
- 持久化影响:`submitted_prompt` 会被 `output_prompt` 复制并写入 `editor_project_resource` 的 prompt 列,因此注入后该列会带上约束句。响应体返回的仍是用户原文 `role_setting`,前端显示不变。角色形象与图标图集本来就存工程化提示词,普通图片从存原文变为存原文加一行。不新增 OSS PUT、项目资源、素材记录或画布图层。
|
||||
- 未覆盖:图标图集尚未约束各素材共用同一像素块大小(`estimate_step_size` 取全图相邻峰间距的第 30 百分位,块大小不一时步长估计会偏);角色形象提示词里既有的「严格基于图1的角色美术视觉规范的美术风格」与像素约束存在潜在冲突,未改写。两项都等实测。snapper 当前无任何日志,`resolve_step_sizes` 走检测还是走统一网格兜底在外部不可观测,注入效果暂时只能靠人工看图判断。
|
||||
- 关联文档:`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md`、`docs/【编辑器】画板角色形象生成入口设计-2026-06-15.md`、`docs/【编辑器】画板图标素材生成入口设计-2026-06-15.md`、`docs/openapi/genarrative-external-v1.openapi.json`。
|
||||
|
||||
@@ -38,7 +38,9 @@
|
||||
|
||||
- 普通 `生成图片`、`生成角色形象` 和 `生成图标素材` 三个面板增加紧凑的 `像素艺术` 勾选项;移动端可独占一行,但不增加功能说明文案。当前生成对象以 `style: "none" | "pixelArt"` 保存选择并随现有请求 / 队列 payload 传递;该字段不写入用户可见 `generationInputs`,也不新增素材元数据字段。其它生成、编辑、UI 素材提取、角色动画及画布 Agent 入口不展示或设置该选项。
|
||||
- `style` 是可选字符串兼容边界。省略、`null`、空字符串和 `"none"` 统一归一为内部 `None`,不返回告警;`"pixelArt"` 仅允许普通图片(`kind` 省略)与 `kind="character"`,图标图集请求单独允许该值。未知字符串或在 `spec / quick-edit / ui-design / publication-material` 等不支持的图片 `kind` 上请求 `"pixelArt"` 时,按 `None` 完成原管线并通过既有通用 `warning` 返回 `unsupported-image-style`;非字符串 JSON 仍是畸形请求并返回 `400`。旧 payload 缺少字段时等价于 `None`。
|
||||
- `None` 必须保持现有生成、尺寸处理、BgFilter、上传、资源和画布链路不变。`PixelArt` 只增加父流程内的纯内存 Rust 后处理,不启动 Python 或独立服务,也不改变 BgFilter 的 `flat` 参数、Alpha 回贴、`cross_check`、fallback 或默认关闭 despill 的现有行为。
|
||||
- `None` 必须保持现有生成、尺寸处理、BgFilter、上传、资源和画布链路不变,包括提交给 provider 的提示词必须与未带该字段时逐字一致。`PixelArt` 在父流程内做两件事:向提交给 provider 的提示词追加一行像素风约束,以及在 provider 回图后执行纯内存 Rust 像素规整。两者都不启动 Python 或独立服务,也不改变 BgFilter 的 `flat` 参数、Alpha 回贴、`cross_check`、fallback 或默认关闭 despill 的现有行为。
|
||||
- 2026-08-01 修订:`PixelArt` 增加提示词端约束。snapper 是几何对齐器,横纵两轴都检测不到网格步长时会退到 `min(width,height)/64` 统一网格兜底,产出马赛克而非像素画;因此在提示词端要求 provider 本身就输出块状结构。注入点固定为 `generate_editor_image_for_owner` 与 `generate_editor_icon_spritesheet_for_owner` 构造提交提示词的位置,包住既有 builder 的返回值,builder 签名和既有输出契约不变,三个入口(登录态路由、外部 API v1、异步 job worker)一处覆盖。约束句按链路分三条、追加在末尾并独立成行:普通图片用「画面为像素风格」;角色形象和图标图集生成后都要按纯色抠像,绿幕底必须保持平整,分别用「角色主体为像素风格」和「每个图标素材均为像素风格」,不得出现「画面」级别的像素化要求。实测只提「像素风格」已可接受,不注入网格密度、色板色数和抗锯齿等约束。该约束句会随 `submitted_prompt` 写入 `editor_project_resource` 的 prompt 列,但响应体返回的仍是用户原文,前端显示不变;不新增 OSS PUT、项目资源、素材记录或画布图层。
|
||||
- 2026-08-01 修订:画布四个生图入口(普通图片与角色形象共用一条、UI 设计图、修改图片的两个 provider 分支)的 negative prompt 移除「低清晰度」。该词按字面否定低分辨率,与以低分辨率重采样为本质的像素风直接对冲。其余玩法的同名词条不在本次范围内。
|
||||
- 普通图片与角色在 provider 回图后先按统一业务像素矩阵尝试交付尺寸归一:允许无放大恢复时使用 Lanczos 重采样并居中裁切,无法安全恢复时保留 provider 实际尺寸并返回非阻断告警。普通图片随后以这张实际交付尺寸图同时作为网格分析源和 RGBA 采样源;角色先持久化同尺寸平底原图并交给 BgFilter,正常成功后把 Alpha 蒙版回贴到该平底原图,再以平底原图分析网格、以透明 RGBA 图采样。图标仍以已持久化的平底 provider 图尺寸为基准,BgFilter 成功并回贴 Alpha 后执行同样的双输入规整。固定首版参数为:分析色数 `16`、Alpha 覆盖阈值 `0.375`、像素格尺寸自动检测、相邻边缘峰间距使用线性插值 `P30` 估算步长、固定色板关闭、K-means 最大采样 `262144`。
|
||||
- 像素规整 CPU 工作使用进程级最大并发 `2`;取得并发许可的排队时间与实际处理时间共享最多 `30` 秒预算,同时不得晚于当前请求 deadline,最终以两者中更早者为准。输入图片任一边不得超过 `10000` 像素,总像素不得超过 `8294400`;超限、排队超时或处理超时均按像素后处理失败的 best-effort 规则保留进入该步骤前的图片。
|
||||
- 单格颜色按 `Σ(A × RGB) / ΣA` 进行 Alpha 加权;单格覆盖率按 `Σ(A / 255) / N` 计算。覆盖率大于等于 `0.375` 且 `ΣA > 0` 时输出硬 Alpha `255`,否则输出严格的 `[0,0,0,0]`;最终 Alpha 只允许 `0 / 255`。分析用 16 色只负责网格识别,不限制最终输出色数。
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
- `model`:支持 `gemini-3.1-flash-image-preview`(UI 显示 `nanobanana2`)和 `gpt-image-2`,默认 `nanobanana2`。
|
||||
- `aspectRatio`:按 `x:y` 展示,选项跟随模型。
|
||||
- `imageSize`:按 `0.5K / 1K / 2K` 展示,选项跟随模型。
|
||||
- `style`:可选生成后处理风格;未勾选像素艺术时传 `"none"`,勾选时传 `"pixelArt"`。
|
||||
- `style`:可选生成风格,同时影响提交给 provider 的提示词和回图后的像素规整;未勾选像素艺术时传 `"none"`,勾选时传 `"pixelArt"`。
|
||||
- `priceMudPoints`:按当前模型和尺寸从编辑器生成计费配置计算;`nanobanana2 1K` 为 `12`,`gpt-image-2 1K` 为 `3`、`gpt-image-2 2K` 为 `5`。前端只提交配置函数计算值,后端用 `editor_generation_config` 校验,不允许素材生成面板自行写死价格。
|
||||
- 模型与尺寸选项:
|
||||
- `nanobanana2`:比例 `1:1 / 4:3 / 3:2 / 2:3 / 9:16 / 16:9`;大小 `0.5K / 1K / 2K`。后端走 `/v1beta/models/{model}:generateContent`,把图标规范图作为 `inline_data`,并把 `aspectRatio` / `imageSize` 写入 `generationConfig.imageConfig`;`0.5K` 按 VectorEngine 文档传 `"512"`。
|
||||
@@ -65,6 +65,7 @@
|
||||
|
||||
- 图标素材面板增加紧凑的 `像素艺术` 勾选项。选择保存于现有生成器快照,并可随现有请求和队列 payload 传递;不写入用户可见 `generationInputs`、素材元数据或新建的持久化记录。
|
||||
- `style` 省略、为 `null`、空字符串或 `"none"` 时按内部 `None` 处理且不告警;`"pixelArt"` 启用像素规整。未知字符串按 `None` 继续生成,并通过既有通用 `warning` 返回 `unsupported-image-style`;非字符串 JSON 仍返回 `400`。
|
||||
- 2026-08-01 修订:`"pixelArt"` 不再只是后处理,同时向提交给 provider 的提示词末尾追加独立一行约束。图标链路使用「每个图标素材均为像素风格」,**不得**使用「画面为像素风格」——图集生成后要按纯色抠像,绿幕底必须保持平整,画面级像素化要求会与同一段提示词里的「纯色背景必须平整无纹理、无渐变」互相拆台;一张图内是多个彼此分离的素材,需要逐个点名,避免模型只把其中一部分做成像素块。注入发生在 `build_editor_icon_spritesheet_prompt` 返回之后,该函数签名和输出契约不变。约束句随提交提示词写入 `editor_project_resource` 的 prompt 列,不新增 OSS PUT、项目资源、图集画布项或切片画布项。尚未约束各素材共用同一像素块大小(`estimate_step_size` 取全图相邻峰间距的第 30 百分位,块大小不一时步长估计会偏),等实测。
|
||||
- 图标链路以已持久化的带纯色背景 provider 原图实际尺寸为基准;BgFilter 正常成功后,把 Alpha 蒙版回贴到该同尺寸平底原图,再执行像素规整。网格分析源使用平底 provider 原图,RGBA 采样源使用 Alpha 已回贴的透明图;规整结果不再经过独立的最终尺寸处理,直接上传透明 spritesheet,成功后才进入原有连通域自动拆分。
|
||||
- 首版固定参数为分析色数 `16`、Alpha 覆盖阈值 `0.375`、像素格尺寸自动检测、固定色板关闭、K-means 最大采样 `262144`。单格颜色按 `Σ(A × RGB) / ΣA` 进行 Alpha 加权;覆盖率 `Σ(A / 255) / N >= 0.375` 且 `ΣA > 0` 时输出硬 Alpha `255`,否则输出严格 `[0,0,0,0]`。分析色数不限制最终输出色数。
|
||||
- 像素规整 CPU 工作使用进程级最大并发 `2`;取得并发许可的排队时间与实际处理时间共享最多 `30` 秒预算,同时不得晚于当前请求 deadline,最终以两者中更早者为准。输入图片任一边不得超过 `10000` 像素,总像素不得超过 `8294400`;超限、排队超时或处理超时均保留 Alpha 已回贴的透明图并走非致命降级,随后仍可进入原有自动拆分。
|
||||
@@ -93,7 +94,7 @@
|
||||
- 默认提示文本会完整进入 prompt;用户输入不再被解析为素材数量。例如“各种敌人头像:骷髅 哥布林 强盗 龙 蝙蝠等”只是一段完整需求,不代表必须生成或拆出 `6` 个素材。
|
||||
- 默认打开图标素材面板时选中 `nanobanana2 / 1:1 / 1K`;模型切换后,角色和图标素材面板之间沿用上次选择的模型。
|
||||
- 图标素材生成请求必须带 `model`、`aspectRatio` 和 `imageSize`;`nanobanana2` 请求体必须包含 `generationConfig.imageConfig.aspectRatio/imageSize`,`gpt-image-2` 请求必须包含文档映射后的 `size`。
|
||||
- 图标素材面板可选择 `style: "none" | "pixelArt"`;`none` 完整保持原处理路径,`pixelArt` 在 Alpha 回贴后、自动拆分前执行内存像素规整,最终 OSS PUT、项目资源、图集画布项和切片画布项数量不得因此增加。
|
||||
- 图标素材面板可选择 `style: "none" | "pixelArt"`;`none` 完整保持原处理路径,且提交给 provider 的提示词与未带该字段时逐字一致,`pixelArt` 在提示词末尾追加「每个图标素材均为像素风格」并在 Alpha 回贴后、自动拆分前执行内存像素规整,最终 OSS PUT、项目资源、图集画布项和切片画布项数量不得因此增加。
|
||||
- 图标素材生成可以上传普通参考图;提交时图标规范图仍走 `referenceImageSrc`,普通参考图走 `referenceImageSrcs`,二者都必须是稳定引用(`objectKey` / 项目资源 ID / 素材 ID),禁止 Data URL / Blob URL,并写入 `generationInputs.references`。
|
||||
- 透明背景处理和自动拆分都成功后,画布同时出现透明 spritesheet 主图、其右侧的 provider 原图,以及从原图右侧铺开的全部有效连通域图标图层,图标依次命名为 `素材 N`;透明图集成功但拆分失败时仍出现透明主图与右侧原图,透明背景处理最终失败时只出现 provider 原图。
|
||||
- 选中透明图集图层时显示 `拆分图集`;点击后源图集显示扫描蒙层与 `拆图中` 状态,工具栏按钮同步切换为旋转图标和 `拆图中` 并禁用重复提交。完成后恢复工具栏,不新增第二张图集,只在 provider 原图右侧追加自动识别的独立素材,并同步写入素材库。
|
||||
|
||||
@@ -65,6 +65,7 @@
|
||||
|
||||
- 角色面板增加紧凑的 `像素艺术` 勾选项,请求使用可选字符串字段 `style`:未勾选传 `"none"`,勾选传 `"pixelArt"`。该选择可以随现有生成器快照和队列 payload 保存,但不写入用户可见 `generationInputs`、素材元数据或新建的持久化记录。
|
||||
- `style` 省略、为 `null`、空字符串或 `"none"` 时按内部 `None` 处理且不告警;`"pixelArt"` 在 `kind="character"` 时启用像素规整。未知字符串按 `None` 继续生成,并通过既有通用 `warning` 返回 `unsupported-image-style`;非字符串 JSON 仍返回 `400`。同一图片生成请求 DTO 被其它 `kind` 复用时,只有普通图片和 `character` 支持 `"pixelArt"`,其它 `kind` 收到该值也按不支持风格降级。
|
||||
- 2026-08-01 修订:`"pixelArt"` 不再只是后处理,同时向提交给 provider 的提示词末尾追加独立一行约束。角色链路使用「角色主体为像素风格」,**不得**使用「画面为像素风格」——角色生成后要按纯色抠像,绿幕底必须保持平整,同一段提示词里已写死「纯色背景必须平整无纹理、无渐变」,画面级像素化要求会与之互相拆台;且该提示词已禁止出现角色以外的场景内容,因此只需点名角色本身。注入发生在 `build_editor_character_image_prompt` 返回之后,该函数签名和输出契约不变。约束句随 `submitted_prompt` 写入 `editor_project_resource` 的 prompt 列,响应体返回的仍是用户原文,前端显示不变。角色提示词里既有的「严格基于图1的角色美术视觉规范的美术风格」与像素约束存在潜在冲突,本次未改写,等实测。
|
||||
- 角色 provider 回图先按统一业务像素矩阵执行交付尺寸归一:允许无放大恢复时使用 Lanczos 重采样并居中裁切,无法安全恢复时保留 provider 实际尺寸并返回非阻断告警。归一后的带纯色背景图先持久化并作为 BgFilter 输入;BgFilter 正常成功后,把 Alpha 蒙版回贴到这张同尺寸平底原图,再执行像素规整并上传透明主图。网格分析源使用已收口到实际交付尺寸的平底原图,RGBA 采样源使用 Alpha 已回贴的透明图;软 Alpha 只参与单格覆盖率和 Alpha 加权 RGB 计算,输出 Alpha 硬化为 `0 / 255`。
|
||||
- 首版参数固定为分析色数 `16`、Alpha 覆盖阈值 `0.375`、像素格尺寸自动检测、固定色板关闭、K-means 最大采样 `262144`。单格覆盖率 `Σ(A / 255) / N >= 0.375` 且 `ΣA > 0` 时输出 `A=255`,颜色按 `Σ(A × RGB) / ΣA` 计算;否则输出 `[0,0,0,0]`。分析色数不限制最终输出色数。
|
||||
- 像素规整 CPU 工作使用进程级最大并发 `2`;取得并发许可的排队时间与实际处理时间共享最多 `30` 秒预算,同时不得晚于当前请求 deadline,最终以两者中更早者为准。输入图片任一边不得超过 `10000` 像素,总像素不得超过 `8294400`;超限、排队超时或处理超时均保留 Alpha 已回贴的透明图并走非致命降级。
|
||||
@@ -118,7 +119,7 @@
|
||||
- `从画布中选择` 后点击已有画布图片可绑定为角色规范,`Esc` 可退出点选状态。
|
||||
- 上传常规参考图后缩略图右下角显示序号。
|
||||
- 输入角色设定并生成时,请求包含 `kind: "character"`、角色设定 prompt、参考图数组、`model`、`screenColor`、`aspectRatio` 和 `imageSize`。
|
||||
- 角色面板可选择 `style: "none" | "pixelArt"`;`none` 的处理路径和产物保持不变,`pixelArt` 在 Alpha 回贴后执行内存像素规整,最终 OSS PUT、项目资源和画布图层数量不得增加。
|
||||
- 角色面板可选择 `style: "none" | "pixelArt"`;`none` 的处理路径和产物保持不变,且提交给 provider 的提示词与未带该字段时逐字一致,`pixelArt` 在提示词末尾追加「角色主体为像素风格」并在 Alpha 回贴后执行内存像素规整,最终 OSS PUT、项目资源和画布图层数量不得增加。
|
||||
- 默认打开角色生成面板时选中 `nanobanana2 / 1:1 / 1K`;切换到 `gpt-image-2` 后再次打开角色或图标素材面板应沿用该模型。
|
||||
- 生成成功后在占位图位置创建 `assetKind: "character"` 图层,右上角显示 `角色` 标签,布局保存包含该字段。
|
||||
|
||||
|
||||
@@ -1743,6 +1743,13 @@ pub(crate) async fn generate_editor_image_for_owner(
|
||||
let pixel_art_supported = matches!(normalized_kind, None | Some("") | Some("character"));
|
||||
let (image_style, mut generation_warning) =
|
||||
normalize_editor_image_generation_style(payload.style.as_deref(), pixel_art_supported);
|
||||
// 中文注释:角色形象带绿幕抠像底色,只像素化角色本身;普通图片没有抠像底色,整幅画面像素化。
|
||||
// ui-design 等 kind 走不到 PixelArt(pixel_art_supported 已挡掉),落到 Image 分支无副作用。
|
||||
let pixel_art_prompt_scope = if is_character_generation {
|
||||
EditorPixelArtPromptScope::Character
|
||||
} else {
|
||||
EditorPixelArtPromptScope::Image
|
||||
};
|
||||
// 背景色决策挪到预扣泥点之后(见下方 execute_billable 闭包),避免余额不足 / 生成注定失败时
|
||||
// 仍白发一次 gpt-5-mini 决策。这里先固化决策需要、但随后会被 payload 消费掉的输入。
|
||||
let requested_screen_color = payload.screen_color.clone();
|
||||
@@ -1888,16 +1895,20 @@ pub(crate) async fn generate_editor_image_for_owner(
|
||||
let screen_color = screen_background_decision
|
||||
.as_ref()
|
||||
.map(|decision| decision.color);
|
||||
let submitted_prompt = if is_character_generation {
|
||||
build_editor_character_image_prompt(
|
||||
role_setting.as_str(),
|
||||
screen_color.expect("character generation should have screen color"),
|
||||
)
|
||||
} else if is_ui_design_generation {
|
||||
build_editor_ui_design_prompt(role_setting.as_str(), ui_has_references)
|
||||
} else {
|
||||
role_setting.clone()
|
||||
};
|
||||
let submitted_prompt = apply_editor_pixel_art_style_prompt(
|
||||
if is_character_generation {
|
||||
build_editor_character_image_prompt(
|
||||
role_setting.as_str(),
|
||||
screen_color.expect("character generation should have screen color"),
|
||||
)
|
||||
} else if is_ui_design_generation {
|
||||
build_editor_ui_design_prompt(role_setting.as_str(), ui_has_references)
|
||||
} else {
|
||||
role_setting.clone()
|
||||
},
|
||||
image_style,
|
||||
pixel_art_prompt_scope,
|
||||
);
|
||||
let generated = if generation_options.model == EDITOR_IMAGE_MODEL_NANOBANANA2 {
|
||||
create_openai_nanobanana_generate_content(
|
||||
&http_client,
|
||||
@@ -5837,7 +5848,11 @@ pub(crate) async fn generate_editor_icon_spritesheet_for_owner(
|
||||
)
|
||||
.await?;
|
||||
let screen_color = screen_background_decision.color;
|
||||
let prompt = build_editor_icon_spritesheet_prompt(&icon_descriptions, screen_color);
|
||||
let prompt = apply_editor_pixel_art_style_prompt(
|
||||
build_editor_icon_spritesheet_prompt(&icon_descriptions, screen_color),
|
||||
image_style,
|
||||
EditorPixelArtPromptScope::IconSpritesheet,
|
||||
);
|
||||
let generated = if generation_options.model == EDITOR_IMAGE_MODEL_NANOBANANA2 {
|
||||
create_openai_nanobanana_generate_content(
|
||||
&http_client,
|
||||
@@ -9188,6 +9203,49 @@ fn build_editor_character_image_prompt(
|
||||
.join("\n")
|
||||
}
|
||||
|
||||
/// 中文注释:`style="pixelArt"` 此前只驱动 provider 返回后的确定性像素规整,完全不参与提示词。
|
||||
/// snapper 是几何对齐器——检测网格步长后按格重采样;provider 交一张柔和渐变图时两轴都测不到
|
||||
/// 步长,生成路径用的 legacy profile 会退到 `min(w,h)/64` 统一网格,产出的是马赛克而不是像素
|
||||
/// 画。这里在提示词端补一句,让 provider 本身就输出块状结构,snapper 从「抢救」变成「对齐」。
|
||||
/// 实测只提「像素风格」效果已可接受,因此不叠加网格密度、色板和抗锯齿等约束。
|
||||
///
|
||||
/// 普通图片没有抠像底色,整幅画面都该像素化。
|
||||
const EDITOR_PIXEL_ART_IMAGE_PROMPT: &str = "画面为像素风格";
|
||||
/// 中文注释:角色形象生成后要用 bgfilter 按纯色抠像,绿幕底必须保持平整;同一段提示词里已经
|
||||
/// 写死了「纯色背景必须平整无纹理、无渐变」和「禁止出现建筑、室内布景、风景、地面道具、漂浮
|
||||
/// 物」,也就是画面里除角色本身没有别的内容。所以只点名角色,不说「画面」。
|
||||
const EDITOR_PIXEL_ART_CHARACTER_PROMPT: &str = "角色主体为像素风格";
|
||||
/// 中文注释:图标图集同样要抠绿幕底,但一张图里是多个彼此分离的图标素材,逐个点名避免模型
|
||||
/// 只把其中一部分做成像素块。
|
||||
const EDITOR_PIXEL_ART_ICON_SPRITESHEET_PROMPT: &str = "每个图标素材均为像素风格";
|
||||
|
||||
/// 中文注释:按链路命名而不是按「整幅画面 / 只有主体」这类抽象作用域分类——三条链路的差异
|
||||
/// 不只是作用域,措辞本身还各自绑定各自提示词里已有的约束。
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
enum EditorPixelArtPromptScope {
|
||||
Image,
|
||||
Character,
|
||||
IconSpritesheet,
|
||||
}
|
||||
|
||||
/// 中文注释:追加在末尾而不是前置——风格约束靠后更容易被遵守,也不会和「生成游戏角色立绘」
|
||||
/// 这类任务声明抢第一位。非 PixelArt 必须原样返回:普通图片路径的提示词就是用户原文。
|
||||
fn apply_editor_pixel_art_style_prompt(
|
||||
prompt: String,
|
||||
style: EditorImageGenerationStyle,
|
||||
scope: EditorPixelArtPromptScope,
|
||||
) -> String {
|
||||
if style != EditorImageGenerationStyle::PixelArt {
|
||||
return prompt;
|
||||
}
|
||||
let clause = match scope {
|
||||
EditorPixelArtPromptScope::Image => EDITOR_PIXEL_ART_IMAGE_PROMPT,
|
||||
EditorPixelArtPromptScope::Character => EDITOR_PIXEL_ART_CHARACTER_PROMPT,
|
||||
EditorPixelArtPromptScope::IconSpritesheet => EDITOR_PIXEL_ART_ICON_SPRITESHEET_PROMPT,
|
||||
};
|
||||
format!("{prompt}\n{clause}")
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
struct EditorGenerationOptions {
|
||||
model: &'static str,
|
||||
@@ -12898,6 +12956,84 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pixel_art_style_prompt_is_scoped_per_generation_path() {
|
||||
// 中文注释:非 PixelArt 必须原样返回——普通图片路径的提示词就是用户原文,
|
||||
// 任何多余追加都会直接落进 editor_project_resource 的 prompt 列。
|
||||
for scope in [
|
||||
EditorPixelArtPromptScope::Image,
|
||||
EditorPixelArtPromptScope::Character,
|
||||
EditorPixelArtPromptScope::IconSpritesheet,
|
||||
] {
|
||||
assert_eq!(
|
||||
apply_editor_pixel_art_style_prompt(
|
||||
"一只猫".to_string(),
|
||||
EditorImageGenerationStyle::None,
|
||||
scope,
|
||||
),
|
||||
"一只猫"
|
||||
);
|
||||
}
|
||||
|
||||
let image_prompt = apply_editor_pixel_art_style_prompt(
|
||||
"一只猫".to_string(),
|
||||
EditorImageGenerationStyle::PixelArt,
|
||||
EditorPixelArtPromptScope::Image,
|
||||
);
|
||||
assert_eq!(image_prompt, "一只猫\n画面为像素风格");
|
||||
|
||||
// 中文注释:角色形象与图标图集生成后都要按纯色抠像,绿幕底必须保持平整,
|
||||
// 因此这两条都不能出现「画面」级别的像素化要求,否则和同一段提示词里的
|
||||
// 「纯色背景必须平整无纹理、无渐变」互相拆台。
|
||||
let character_prompt = apply_editor_pixel_art_style_prompt(
|
||||
"一只猫".to_string(),
|
||||
EditorImageGenerationStyle::PixelArt,
|
||||
EditorPixelArtPromptScope::Character,
|
||||
);
|
||||
assert_eq!(character_prompt, "一只猫\n角色主体为像素风格");
|
||||
|
||||
let icon_prompt = apply_editor_pixel_art_style_prompt(
|
||||
"一只猫".to_string(),
|
||||
EditorImageGenerationStyle::PixelArt,
|
||||
EditorPixelArtPromptScope::IconSpritesheet,
|
||||
);
|
||||
assert_eq!(icon_prompt, "一只猫\n每个图标素材均为像素风格");
|
||||
|
||||
for scoped_prompt in [character_prompt.as_str(), icon_prompt.as_str()] {
|
||||
assert!(!scoped_prompt.contains(EDITOR_PIXEL_ART_IMAGE_PROMPT));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pixel_art_style_prompt_is_applied_before_provider_call() {
|
||||
let source = include_str!("editor_project.rs");
|
||||
|
||||
// 中文注释:注入必须发生在 provider 调用之前——一旦被挪到之后,提示词约束就完全
|
||||
// 不起作用,而产物看起来仍然「成功」,只是退化成 snapper 的统一网格马赛克。
|
||||
assert_function_contains_in_order(
|
||||
source,
|
||||
"pub(crate) async fn generate_editor_image_for_owner",
|
||||
"fn normalize_editor_image_generation_size",
|
||||
&[
|
||||
"let pixel_art_prompt_scope = if is_character_generation {",
|
||||
"EditorPixelArtPromptScope::Character",
|
||||
"EditorPixelArtPromptScope::Image",
|
||||
"apply_editor_pixel_art_style_prompt(",
|
||||
"create_openai_",
|
||||
],
|
||||
);
|
||||
assert_function_contains_in_order(
|
||||
source,
|
||||
"pub(crate) async fn generate_editor_icon_spritesheet_for_owner",
|
||||
"pub async fn extract_editor_ui_design_assets",
|
||||
&[
|
||||
"apply_editor_pixel_art_style_prompt(",
|
||||
"EditorPixelArtPromptScope::IconSpritesheet",
|
||||
"create_openai_",
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn editor_character_image_prompt_appends_user_role_setting() {
|
||||
let prompt = build_editor_character_image_prompt(
|
||||
|
||||
Reference in New Issue
Block a user