diff --git a/dev/theme-builder/README.md b/dev/theme-builder/README.md index 0f2f156..bb455b8 100644 --- a/dev/theme-builder/README.md +++ b/dev/theme-builder/README.md @@ -70,7 +70,29 @@ http://127.0.0.1:8777/dev/theme-builder/ (深色块特异性更高,本来就压住它)。 **图片池** —— 上传任意张,浏览器本地转 WebP,画布尺寸 = 原图尺寸(不缩放、不留透明边)。 -导出时只打包实际用到的图。 +导出时只打包实际用到的图。每张图左上角有 `fx` 按钮,进处理面板(处理过的会高亮)。 + +### 图片处理 + +| 功能 | 说明 | +| --- | --- | +| 不透明度 | 0~100%。CSS 背景图没法单独设 `opacity`,只能烘焙进像素 | +| 压白 / 雾度 | `v×(1-k)+255×k`。场景压白 38% 时人物常用 12~26%,否则是全页对比度最高的一块 | +| 去色 | 往灰度混。彩色素材压不住素场景时用 | +| 裁掉透明边 | 生成图四周留白多时裁掉,否则按高度定尺寸会让主体看着比实际小 | +| 去白底 | 四边漫水 + 封闭白孔检测,默认容差 16 | +| 边缘收 1px | 去白底后把残留的半透明白边收掉,代价是轮廓缩一圈 | + +**非破坏**:原文件字节存在 `orig` 里,`fx` 只存参数,每次处理都从 `orig` 重算。 +所以「压白 16% → 改成 20%」不会叠加两次压缩,反复调参也不会把边缘越磨越糊。 +打开面板看一眼就关,一个像素都不会动(参数没变就跳过重编码)。 + +**去白底的两个关键点**(都是踩过的): + +- **封闭白孔**:手内侧、两腿之间那种被轮廓包住的白色,四边漫水根本进不去, + 必须单独按连通域掏(面积 ≥ 24 才掏,更小的多半是眼睛高光)。可以关掉。 +- **容差别贪**:背景常见 253,白袜子 237,只差 16。容差 16 保得住袜子,30 就吃穿了。 + 实测白底立绘去白底后透明占比 46.3%,与手写 floodfill 抠出来的 46.6% 差 0.3%。 ## 工程文件与草稿 @@ -104,7 +126,7 @@ INSTALL-.txt ## 它不做什么 -- **不抠图**:图片必须本身是透明底,白底图导出的主题会带一块白。 +- **不做复杂修图**:没有笔刷、蒙版、自由裁切、调色曲线 —— 这些该在专门的工具里做完再进来。 - **不做调色**:想统一「雾度」(人物压白 16%、场景 30~38%)得在生成端做, 见 `docs/theme-shiroko-assets-spec.md`。 - **不写配置**:`[theme] available` 只给要粘贴的那一行。 @@ -123,6 +145,13 @@ INSTALL-.txt ?dump=zip 把 zip 的 base64 写进
 ?dump=validate     把 validate() 的结果写进 
 ?dump=install      把安装说明写进 
+?dump=fx           跑处理算法自检(13 个断言,输出 JSON)
+?dump=fxpipe       跑弹窗链路冒烟(打开→改参→预览→落盘→无改动不重编码)
+?dump=fxkey        端到端去白底:载入白底图,统计透明像素比例
+?loadurl=/static/images/x.webp&fx=x
+                   直接把项目里的图当素材载入(不必先下载再上传),fx= 打开处理面板
+?fxjson={"key":{"on":true}}
+                   配合 loadurl 给初始处理参数
 ```
 
 ```bash
diff --git a/dev/theme-builder/index.html b/dev/theme-builder/index.html
index cea4aeb..9413ea9 100644
--- a/dev/theme-builder/index.html
+++ b/dev/theme-builder/index.html
@@ -108,6 +108,43 @@
     position:absolute;inset:auto 0 0 0;font-size:9px;text-align:center;background:#000a;color:#cbd6e4;
     padding:1px 0;
   }
+  /* 处理入口常驻(不靠 hover):处理过的素材要能一眼看出来,否则
+     同一张图有「原图」和「压白 16% 版」两副样子,改完就分不清哪张是现在的 */
+  .as .fx{
+    position:absolute;top:2px;left:1px;font-size:10px;line-height:1;font-weight:600;
+    color:#cbd6e4;background:#0009;border-radius:4px;padding:2px 3px;cursor:pointer;
+  }
+  .as .fx:hover{background:var(--ui-accent);color:#08121c}
+  .as .fx.on{background:var(--ui-accent);color:#08121c}
+
+  /* ---------- 图片处理弹窗 ---------- */
+  .modal{position:fixed;inset:0;background:#000b;z-index:50;display:none;align-items:center;justify-content:center}
+  .modal.on{display:flex}
+  .mbox{
+    width:min(900px,94vw);max-height:90vh;display:flex;flex-direction:column;
+    background:var(--ui-panel);border:1px solid var(--ui-line);border-radius:10px;
+    overflow:hidden;box-shadow:0 24px 64px #000a;
+  }
+  .mbox header{display:flex;align-items:center;gap:10px;padding:10px 14px;border-bottom:1px solid var(--ui-line)}
+  .mbox header .t{font-weight:600}
+  .mbox header .sub{font-size:11px;color:var(--ui-text2)}
+  .mbox header .spacer{flex:1}
+  .mbody{display:grid;grid-template-columns:minmax(0,1fr) 252px;flex:1;min-height:0}
+  .mprev{
+    display:flex;align-items:center;justify-content:center;padding:18px;min-height:0;min-width:0;
+    /* 棋盘格:不透明度是这一屏唯一看不出来的东西,必须有背景参照 */
+    background:conic-gradient(#1a2029 25%, #0d1117 0 50%, #1a2029 0 75%, #0d1117 0) 0 0/16px 16px;
+  }
+  .mprev canvas{max-width:100%;max-height:58vh;display:block;box-shadow:0 6px 20px #0006}
+  .mparams{padding:14px;border-left:1px solid var(--ui-line);overflow:auto}
+  .fxrow{margin-bottom:13px}
+  .fxrow .lab{display:flex;justify-content:space-between;align-items:baseline;font-size:11px;color:var(--ui-text2);margin-bottom:2px}
+  .fxrow .lab b{color:var(--ui-text);font-weight:600;font-variant-numeric:tabular-nums}
+  .fxrow input[type=range]{width:100%;accent-color:var(--ui-accent);margin:0}
+  .fxrow .sub{font-size:10px;color:#6f7c8d;margin-top:2px}
+  .fxrow.chkrow{margin-bottom:8px}
+  .mbox footer{display:flex;align-items:center;gap:8px;padding:10px 14px;border-top:1px solid var(--ui-line)}
+  .mbox footer .info{flex:1;font-size:11px;color:var(--ui-text2)}
 
   .pal{display:flex;flex-direction:column;gap:5px}
   .pal .p{display:flex;align-items:center;gap:8px;font-size:11px;color:var(--ui-text2)}
@@ -208,6 +245,28 @@
   
 
 
+
+
+