Files
InpaintRegionEditor Dev 23c748bcf0 feat: 自定义图像渲染 + 选区框绘制 + 拖动功能
主要更改:
- 重写前端 extension.js,完全自己控制图像渲染
- 去掉 nodes.py 的 image_upload,避免双图像问题
- 实现选区框绘制(橙色)+ 遮罩区域显示(红色)
- 实现选区框拖动功能
- 修复 onMouseDown 参数格式: (e, pos, canvas)
- 更新文档和开发日志
2026-03-02 22:31:54 +08:00

396 lines
10 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# InpaintRegionEditor 技术验证清单
> 版本:1.0 | 日期:2026-03-02
> 目的:验证所有不确定的技术点,为最终方案提供依据
> 状态:✅ 已完成核心验证
---
## 零、验证结论摘要
### ✅ 已验证可行的方案
| 功能 | 方案 | 来源 |
|------|------|------|
| 图像导出 | `app.activeDocument.saveToOE("png")` | Photopea 官方 API |
| 从选区创建蒙版 | 创建临时图层 + 填充黑白 | sd-webui-photopea-embed |
| 单独导出选中图层 | 隐藏其他图层 + saveToOE | sd-webui-photopea-embed |
### ❌ 已确认不可行
| 功能 | 原因 | 备选方案 |
|------|------|---------|
| 直接访问 layer.mask | API 返回 null (Issue #7341) | 使用选区 workaround |
| 检测图层是否有蒙版 | layer.mask 始终为 null | 让用户通过选区操作 |
### 📋 推荐工作流
**用户创建蒙版的方式**:
1. 在 Photopea 中使用选择工具创建选区
2. 点击"从选区创建蒙版"按钮
3. 系统自动创建黑白蒙版图层并导出
4. 或者:用户直接上传带 Alpha 通道的 PNG
---
## 一、Photopea 加载验证
### 1.1 同时加载原图 + 蒙版
**测试目标**:能否让 Photopea 同时加载两个文件,并且蒙版作为图层蒙版?
**测试脚本**:
```javascript
// 方法 1:先打开图,再加载蒙版作为 mask
app.open("image.png");
var doc = app.activeDocument;
var layer = doc.activeLayer;
app.open("mask.png");
var maskDoc = app.activeDocument;
// 复制 mask 到原图的 layer.mask
```
**预期结果**:
- ✅ 成功:图层右侧出现蒙版缩略图
- ❌ 失败:两张独立的图,或者蒙版只是普通图层
**优先级**:🔴 最高(决定整个工作流)
---
### 1.2 加载带 Alpha 通道的 PNG
**测试目标**:如果上传的 PNG 本身带 Alpha 通道,Photopea 能否自动识别为蒙版?
**测试方法**:
1. 准备一个 RGBA PNG(RGB 是图像,Alpha 是蒙版)
2. 用 `app.open("data:image/png;base64,...")` 加载
3. 检查 `layer.mask` 是否存在
**预期结果**:
- ✅ 成功:Alpha 通道自动转为图层蒙版
- ❌ 失败:Alpha 通道只是透明,不是蒙版
---
### 1.3 用脚本创建图层蒙版
**测试目标**:能否用脚本把一张黑白图转换为图层蒙版?
**测试脚本**:
```javascript
// 假设已经有一个黑白图在剪贴板
doc.selection.load("mask.png");
layer.addMask(true);
```
**参考**:DEVLOG 中提到的 `loadTransparentPixels() + addMask()`
---
## 二、Photopea 导出验证
### 2.1 导出带蒙版的图像
**测试目标**:如果图层有蒙版,导出 PNG 时能否保留蒙版到 Alpha 通道?
**测试方法**:
1. 创建一个带蒙版的图层
2. 执行 `app.activeDocument.saveToOE("png")`
3. 检查导出的 PNG 是否有 Alpha 通道
**预期结果**:
- ✅ 成功:PNG 的 Alpha = 蒙版
- ❌ 失败:蒙版被丢弃,或者蒙版渲染到 RGB 里
---
### 2.2 单独导出蒙版
**测试目标**:能否只导出蒙版(黑白图)?
**❌ 原方案失败**:
```javascript
var mask = layer.mask; // ⚠️ 已知返回 null
doc.activeChannel = layer.mask; // 无法执行
```
**✅ Workaround 方案(已验证)**:
参考 `sd-webui-photopea-embed` 的 `createMaskFromSelection()` 实现:
```javascript
// 从选区创建蒙版图层
function createMaskFromSelection() {
if (app.activeDocument.selection === null) {
app.echo("No selection!");
return;
}
// 创建临时图层
newLayer = app.activeDocument.artLayers.add();
newLayer.name = "TempMaskLayer";
// 选区外部填充黑色
app.activeDocument.selection.invert();
color = new SolidColor();
color.rgb.red = 0;
color.rgb.green = 0;
color.rgb.blue = 0;
app.activeDocument.selection.fill(color);
// 选区内部填充白色
color.rgb.red = 255;
color.rgb.green = 255;
color.rgb.blue = 255;
app.activeDocument.selection.invert();
app.activeDocument.selection.fill(color);
}
// 导出蒙版图层
function exportMaskLayer() {
// 隐藏其他图层,只显示蒙版图层
// ... (参考 exportSelectedLayerOnly)
app.activeDocument.saveToOE("png");
// 删除临时蒙版图层
app.activeDocument.activeLayer.remove();
}
```
**验证来源**:`sd-webui-photopea-embed` 项目 (https://github.com/yankooliveira/sd-webui-photopea-embed)
**结论**:✅ 可以通过选区 workaround 实现蒙版导出
---
### 2.3 检测当前是否有蒙版
**测试目标**:在用户点击"保存"前,能否检测当前文档是否有蒙版?
**测试脚本**:
```javascript
var hasMask = (app.activeDocument.activeLayer.mask != null);
// 或者
var hasMask = (app.activeDocument.activeLayer.layerMask != null);
```
**预期结果**:
- ✅ 成功:能准确检测
- ❌ 失败:无法检测(因为 layer.mask 总是返回 null)
**影响**:如果无法检测,就无法在保存前提示用户
---
## 三、前端交互验证
### 3.1 检测用户是否禁用了蒙版
**测试场景**:
1. 用户在 Photopea 里编辑
2. 点击图层蒙版眼睛图标(禁用蒙版)
3. 点保存
**问题**:
- 这时应该保存什么?
- 原图(忽略蒙版)还是报错?
**技术难点**:
- Photopea 的 UI 状态(哪个图层可见、蒙版是否启用)无法通过 API 获取
- 只能通过导出后的结果反推
---
### 3.2 两次导出 + 两次上传
**测试目标**:连续导出两次(图像 + 蒙版),能否都成功上传?
**测试流程**:
```javascript
// 1. 导出图像
var imageBlob = await exportImage();
var imageResult = await uploadToComfyUI(imageBlob, "edited.png");
// 2. 导出蒙版
var maskBlob = await exportMask();
var maskResult = await uploadToComfyUI(maskBlob, "mask.png");
// 3. 更新节点
node.widgets.find(w => w.name === "image").value = imageResult.name;
node.widgets.find(w => w.name === "mask").value = maskResult.name;
```
**潜在问题**:
- Photopea 的 postMessage 是异步的,两次导出会不会冲突?
- 上传需要时间,UI 上怎么显示进度?
---
## 四、后端处理验证
### 4.1 从两个文件合成一个 PNG
**测试目标**:后端能否把 RGB 图 + 黑白蒙版图 合成到一个 PNG?
**测试代码**:
```python
from PIL import Image
# 加载 RGB 图
rgb_img = Image.open("edited.png").convert("RGB")
# 加载蒙版图(黑白)
mask_img = Image.open("mask.png").convert("L") # 转为灰度
# 合成
rgb_img.putalpha(mask_img)
rgb_img.save("combined.png")
```
**预期结果**:
- ✅ 成功:combined.png 的 Alpha = 蒙版
- ❌ 失败:尺寸不匹配、格式问题
---
### 4.2 节点同时输出两个文件名
**测试方案 A**:两个独立的 widget
```python
"image": (sorted(files), {"image_upload": True}),
"mask": (sorted(files), {"image_upload": True}),
```
**测试方案 B**:一个 widget 存 JSON
```python
"files": ("STRING", {"default": '{"image": "", "mask": ""}'}),
```
**测试方案 C**:一个 widget + node.properties
```python
"image": (sorted(files), {"image_upload": True}),
# mask 存在 node.properties["mask_file"]
```
---
## 五、UI/UX 验证
### 5.1 保存按钮的设计
**选项 A**:两个按钮
```
[💾 保存图像] [🎭 保存蒙版]
```
- 优点:用户明确知道在保存什么
- 缺点:步骤多,可能忘记保存另一个
**选项 B**:一个按钮,自动两次导出
```
[💾 保存全部]
```
- 优点:一键完成
- 缺点:慢,失败时难以定位
**选项 C**:下拉菜单
```
[💾 保存 ▼]
├─ 保存图像
├─ 保存蒙版
└─ 保存全部
```
- 优点:灵活
- 缺点:UI 复杂
---
### 5.2 错误提示
**场景**:
1. 用户没画蒙版就点"保存蒙版"
2. Photopea 里没有活动选区
3. 上传失败
**提示方式**:
- A) alert() 弹窗(简单但打断体验)
- B) 状态栏文字(不打断但可能忽略)
- C) Toast 通知(折中方案)
---
## 六、优先级排序
| 编号 | 测试项 | 优先级 | 工作量 | 依赖关系 |
|-----|-------|-------|-------|---------|
| 1.1 | 同时加载原图 + 蒙版 | 🔴 最高 | 2h | 无 |
| 1.2 | 加载带 Alpha 的 PNG | 🔴 最高 | 1h | 无 |
| 2.1 | 导出带蒙版的 PNG | 🔴 最高 | 1h | 1.1 |
| 2.2 | 单独导出蒙版 | 🔴 最高 | 2h | 1.1 |
| 2.3 | 检测是否有蒙版 | 🟡 中 | 1h | 1.1 |
| 4.1 | 后端合成 PNG | 🟡 中 | 1h | 2.1+2.2 |
| 3.2 | 两次导出上传 | 🟡 中 | 2h | 2.1+2.2 |
| 4.2 | 节点输出两个文件名 | 🟢 低 | 1h | 4.1 |
| 5.1 | UI 按钮设计 | 🟢 低 | 1h | 所有 |
---
## 七、下一步行动
### Phase 1:核心验证(1-2 天)
1. 创建测试脚本 `test_photopea.js`
2. 手动测试 1.1, 1.2, 2.1, 2.2
3. 记录结果到本文档
### Phase 2:原型开发(2-3 天)
基于测试结果,选择可行的方案
开发最小可用原型(MVP)
### Phase 3:集成测试(1-2 天)
与 ComfyUI 集成
测试完整工作流
---
## 八、已知限制与解决方案
### 8.1 Photopea API 限制
| 限制 | 影响 | 解决方案 |
|------|------|---------|
| `layer.mask` 返回 null | 无法直接访问图层蒙版 | 使用选区创建蒙版图层 |
| 无法获取 UI 状态 | 无法检测蒙版是否启用 | 信任用户操作 |
| postMessage 异步 | 需要正确处理回调 | Promise 封装 |
### 8.2 推荐的蒙版创建工作流
**方案 A:用户上传带 Alpha 的 PNG(最简单)**
```
用户 → 外部工具创建蒙版 → 上传 PNG → 节点自动提取 Alpha
```
**方案 B:在 Photopea 中通过选区创建(已实现)**
```
用户 → Photopea 选择工具 → 创建选区 → "从选区创建蒙版"按钮 → 自动导出
```
**方案 C:使用 ComfyUI 系统蒙版工具**
```
用户 → 系统蒙版编辑器 → 保存 → 节点使用
```
---
## 九、实现状态
| 功能 | 状态 | 文件 |
|------|------|------|
| 后端节点(padding + 无蒙版报错) | ✅ 已实现 | `nodes.py` |
| 选区框 Canvas UI | ✅ 已实现 | `region-box-editor.js` |
| Photopea 图像编辑 | ✅ 已实现 | `extension.js` |
| Photopea 蒙版导出 | ⚠️ Workaround 可行 | 待添加选区功能 |
---
*文档创建时间:2026-03-02*
*验证完成时间:2026-03-02*
*验证来源:Photopea 官方文档 + sd-webui-photopea-embed 项目*