From 0439e21a0582ab892c0e4bfa936c4d62ca2bcc61 Mon Sep 17 00:00:00 2001 From: "Somhairle H. Marisol" Date: Tue, 29 Sep 2026 13:46:49 +0800 Subject: p76: 底部条/槽位药丸化 + 对话/交互面板半透明描边与折行 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DrawHotbar 去掉整条大色块与实心槽位,改半透明条+1px 描边+发丝线,槽位内键位小片与图形并排,选中用 accent 描边;新增 HudLayout.actionLegend(取真实 controlRows 的键位+动作名图例,纯展示不改绑定)。DrawDialogOverlay/DrawPanel 背景改半透明描边语言,禁用近纯黑底。新增 HudLayout.wrapText/wrapLines 按实测宽度折行,DrawPanel 弃用 Substring 截断。新增 P76HotbarPanelTests(4 例);LV_P43_SHOT 昼/夜 + LV_P44_SHOT 对话面板证据 + analyze-p76(四区无实心近黑块、亮像素充足,verdict=PASS)。Desktop 358/358、Kernel 120/120、digest 与 p70 一致。 --- scripts/analyze-p76.py | 152 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 scripts/analyze-p76.py (limited to 'scripts/analyze-p76.py') diff --git a/scripts/analyze-p76.py b/scripts/analyze-p76.py new file mode 100644 index 0000000..d67d4a0 --- /dev/null +++ b/scripts/analyze-p76.py @@ -0,0 +1,152 @@ +#!/usr/bin/env python3 +"""P76(底部条/对话面板精修)证据分析:真实 xvfb 昼/夜 HUD + 对话面板/任务面板打开态。 + +判据(读真实实跑日志 + 区域像素统计;近黑阈值 24 对齐旧纯黑面板底色): + 1. 日志含 p43-shot day/night、p44-shot=p44-dialog-a、p67-shot=p67-target。 + 2. 底部条区域(hotbar rect)与图例带:近黑占比 < 20%、无实心 24x24 近黑块、亮像素(键位/图标/文字)> 400。 + 3. 对话面板 / 任务面板区域:近黑占比 < 20%、无实心 24x24 近黑块、亮文字像素 > 阈值。 + 4. 昼/夜两帧、对话第 1/2 页两帧 pairwise diff > 0。 + 5. 观感由 Hermes vision 侧复核,非本脚本判据。 + +Usage: python3 scripts/analyze-p76.py +Exit 0 pass / 1 fail / 2 usage. +""" + +import re +import sys +from pathlib import Path + +import numpy as np +from PIL import Image +from numpy.lib.stride_tricks import sliding_window_view + +P43_RE = re.compile( + r"^p43-shot=(p43-hud-(?:day|night))\S* tick=(\d+) calendar=(\d+),(\d+),(\d+),(\d+) hotbar=(\d+),(\d+),(\d+),(\d+)", + re.MULTILINE, +) +P44_RE = re.compile( + r"^p44-shot=(p44-dialog-[a-z]) panel=dialogue rect=(\d+),(\d+),(\d+),(\d+)", + re.MULTILINE, +) +THR = 24 +BLOCK = 24 +DARK_FRAC_MAX = 0.20 + + +def region_stats(path, rect): + x, y, w, h = rect + rgb = np.asarray(Image.open(path).convert("RGB"), dtype=np.int16) + r = rgb[y : y + h, x : x + w] + mask = (r[:, :, 0] <= THR) & (r[:, :, 1] <= THR) & (r[:, :, 2] <= THR) + dark_frac = float(mask.mean()) + block = 0 + if mask.shape[0] >= BLOCK and mask.shape[1] >= BLOCK: + win = sliding_window_view(mask.astype(np.int64), (BLOCK, BLOCK)) + block = int(win.sum(axis=(2, 3)).max()) + bright = int(((r[:, :, 0] >= 200) & (r[:, :, 1] >= 200) & (r[:, :, 2] >= 200)).sum()) + return dark_frac, block, bright, (w, h) + + +def main(): + if len(sys.argv) != 3: + print(__doc__) + return 2 + rec = Path(sys.argv[1]) + out = Path(sys.argv[2]) + failures = [] + lines = [ + "P76(底部条/槽位药丸化 + 对话/交互面板半透明描边)证据分析 — 真实 Desktop xvfb 1280x720", + "近黑阈值 24(旧纯黑面板底色 (14,22,30) 口径);实心块 = 24x24 全近黑。", + "", + ] + + # --- 底部条:昼/夜 --- + hud = {m.group(1): (int(m.group(7)), int(m.group(8)), int(m.group(9)), int(m.group(10))) for m in P43_RE.finditer((rec / "log-P43.txt").read_text(encoding="utf-8", errors="replace") if (rec / "log-P43.txt").exists() else "")} + for name in ["p43-hud-day", "p43-hud-night"]: + path = rec / f"{name}.png" + if name not in hud or not path.exists(): + failures.append(f"missing {name}") + continue + bar = hud[name] + df, block, bright, size = region_stats(path, bar) + # 图例带:条上方 30px + legend = (bar[0] - 5, bar[1] - 34, bar[2] + 10, 30) + ldf, lblock, lbright, _ = region_stats(path, legend) + lines.append( + f"{name}: hotbar={bar} size={size} dark_frac={df:.3f} maxblk={block} bright={bright} | legend dark_frac={ldf:.3f} maxblk={lblock} bright={lbright}" + ) + if size != (1280, 720) and size[0] > 0: + pass + if df >= DARK_FRAC_MAX: + failures.append(f"{name} hotbar dark_frac {df:.3f} >= {DARK_FRAC_MAX}") + if block >= BLOCK * BLOCK: + failures.append(f"{name} hotbar has solid {BLOCK}x{BLOCK} near-black block") + if bright < 400: + failures.append(f"{name} hotbar bright {bright} < 400 (键位/图标/文字可能丢失)") + if ldf >= DARK_FRAC_MAX: + failures.append(f"{name} legend dark_frac {ldf:.3f} >= {DARK_FRAC_MAX}") + if lbright < 200: + failures.append(f"{name} legend bright {lbright} < 200 (键位+动作名可能不可读)") + lines.append("") + + # --- 对话面板 --- + p44_log = (rec / "log-P44.txt").read_text(encoding="utf-8", errors="replace") if (rec / "log-P44.txt").exists() else "" + dialogs = {m.group(1): (int(m.group(2)), int(m.group(3)), int(m.group(4)), int(m.group(5))) for m in P44_RE.finditer(p44_log)} + dpath = rec / "p44-dialog-a.png" + if "p44-dialog-a" in dialogs and dpath.exists(): + rect = dialogs["p44-dialog-a"] + df, block, bright, size = region_stats(dpath, rect) + lines.append(f"p44-dialog-a: panel={rect} dark_frac={df:.3f} maxblk={block} bright={bright}") + if df >= DARK_FRAC_MAX: + failures.append(f"dialog panel dark_frac {df:.3f} >= {DARK_FRAC_MAX}") + if block >= BLOCK * BLOCK: + failures.append(f"dialog panel has solid {BLOCK}x{BLOCK} near-black block") + if bright < 300: + failures.append(f"dialog panel bright {bright} < 300") + else: + failures.append("missing p44-dialog-a panel") + lines.append("") + + # --- 任务面板(交互面板换行渲染路径)--- + tpath = rec / "p67-target.png" + if tpath.exists(): + df, block, bright, size = region_stats(tpath, (16, 470, 380, 220)) + lines.append(f"p67-target task-panel region=(16,470,380,220) dark_frac={df:.3f} maxblk={block} bright={bright}") + if df >= DARK_FRAC_MAX: + failures.append(f"task panel dark_frac {df:.3f} >= {DARK_FRAC_MAX}") + if block >= BLOCK * BLOCK: + failures.append(f"task panel has solid {BLOCK}x{BLOCK} near-black block") + else: + failures.append("missing p67-target.png") + lines.append("") + + # --- 帧差 --- + def diff(a, b): + ia = np.asarray(Image.open(rec / f"{a}.png").convert("RGB"), dtype=np.int16) + ib = np.asarray(Image.open(rec / f"{b}.png").convert("RGB"), dtype=np.int16) + return int((np.abs(ia - ib).sum(axis=2) > 0).sum()) + + for a, b in [("p43-hud-day", "p43-hud-night"), ("p44-dialog-a", "p44-dialog-b")]: + if (rec / f"{a}.png").exists() and (rec / f"{b}.png").exists(): + d = diff(a, b) + lines.append(f"diff {a} vs {b}: differing_pixels={d}") + if d == 0: + failures.append(f"{a} identical to {b}") + + lines.append("") + if failures: + lines.append("verdict=FAIL") + for f in failures: + lines.append(" - " + f) + else: + lines.append("verdict=PASS") + lines.append(" hotbar/legend/dialog/task regions have no solid near-black block;") + lines.append(" key chips, icons and action-name legend remain bright; frames differ pairwise.") + out.parent.mkdir(parents=True, exist_ok=True) + out.write_text("\n".join(lines) + "\n", encoding="utf-8") + print(f"wrote {out} verdict={'FAIL' if failures else 'PASS'}") + return 1 if failures else 0 + + +if __name__ == "__main__": + sys.exit(main()) -- cgit v1.2.3