iter48-卡2:卡②(立绘显示档批): 九件素材原稿分辨率入包(紧裁零降采样,STANDEE_SRC_H九键+产物计数自证)+standee_motion显示档PORTRAIT_DISPLAY_H九键钉值(_place_portrait按档算倍率,缺键push_error按1×)+portrait_probe扩display_size/display_scale+闸件standee_asset_check换口径(源高==原稿表值/显示宽≤150酋长157例外逐字)+样张站桩段按显示档取数(A4解冻重校88×92,V10并排30/30)+V13–V18全绿(50checks)+三面对照图
This commit is contained in:
1 parent
1e35ab947b
commit
4c3f0f6082
9 files changed
+255
-119
No files matched your search
@@ -173,7 +173,8 @@ func _build_status_bar(box: VBoxContainer) -> void:
|
||||
strip.add_child(abandon_btn)
|
||||
|
||||
|
||||
## 头像域:定高 48px=1×、宽随源图比自适应(源 46×48 → 46×48;纵横异倍消除)
|
||||
## 头像域:定高 48px 框、宽随源图比自适应(源 = 原稿分辨率;该面为独立消费面——
|
||||
## 显示档批不改框尺寸,画面随素材分辨率自动变化)
|
||||
func _build_face_rect() -> TextureRect:
|
||||
var face := TextureRect.new()
|
||||
var face_tex := UiKit.load_tex(UiKit.TEX_HERO_PORTRAIT)
|
||||
|
||||
@@ -281,7 +281,11 @@ func _portrait_glyph() -> String:
|
||||
|
||||
# 立绘落位探针出口(站桩纹理非空断言面;缺位回退链保持的对照面)
|
||||
func portrait_probe() -> Dictionary:
|
||||
return {"has_tex": mo._portrait_sprite != null and mo._portrait_sprite.texture != null}
|
||||
var sp: Sprite2D = mo._portrait_sprite
|
||||
if sp == null or sp.texture == null:
|
||||
return {"has_tex": false, "display_size": Vector2.ZERO, "display_scale": 0.0}
|
||||
var src := Vector2(sp.texture.get_width(), sp.texture.get_height())
|
||||
return {"has_tex": true, "display_size": src * sp.scale, "display_scale": sp.scale.x}
|
||||
|
||||
|
||||
# ---- 主角三态切换(攻击演出→attack/受击→hit/结算回 idle;§8.4 帧数档挂点) ----
|
||||
|
||||
@@ -12,6 +12,15 @@ const _HS_VALUE := {"idle": HeroState.IDLE, "reset": HeroState.RESET, "attack":
|
||||
var _owner: Node2D = null
|
||||
var _portrait_sprite: Sprite2D = null # 资产挂点(null = 未落位,字形在位)
|
||||
var _portrait_tex: Texture2D = null # 落位纹理引用(set_portrait_texture 装载;字形回退判据)
|
||||
# 站桩立绘显示档(三档绝对高——art-bible §8.2 显示档定值单点;九键逐键钉值,禁兜底字面量):
|
||||
# 小型 96 = 地精/巨蜘蛛/灰矮人;中型 144 = 主角/钩爪恐魔/熊地精;大型 188 = 酋长/卓尔/石君王。
|
||||
# 显示倍率 = 档高 / 源高(宽随源比例);源高 = 原稿表值(Python 侧 STANDEE_SRC_H——
|
||||
# 跨语言逐键对账由 standee_present_values_test V17 承担)。
|
||||
const PORTRAIT_DISPLAY_H := {
|
||||
"goblin": 96.0, "giant_spider": 96.0, "duergar": 96.0,
|
||||
"hook_horror": 144.0, "bugbear": 144.0, "hero": 144.0,
|
||||
"ogre_chief": 188.0, "drider": 188.0, "duergar_king": 188.0,
|
||||
}
|
||||
# 主角三态切换:state 名 → 纹理(battle_stage_view 组合点注册);缺态=保持现纹理
|
||||
# (缺位回退链延伸——三态素材缺任一不炸不空)。敌站桩不注册不消费。
|
||||
var _hero_tex := {}
|
||||
@@ -35,8 +44,9 @@ func _init(owner: Node2D) -> void:
|
||||
|
||||
|
||||
# 资产挂点:tex 落位后字形隐退、纹理立绘顶位——零代码换装契约;tex=null 回退字形。
|
||||
# flip = 立绘朝向翻转(素材基准朝左时传 true 呈现朝右)。盒高 96px = 48px 立绘源整数 2×
|
||||
# (圣经 §8.2 禁非整数缩放)——源换代时随源重算。(box = 宿主冻结 var,由宿主传入)
|
||||
# flip = 立绘朝向翻转(素材基准朝左时传 true 呈现朝右)。显示高 = PORTRAIT_DISPLAY_H
|
||||
# 逐键绝对高(art-bible §8.2 三档),宽随源比例(高定宽随)。
|
||||
# (box = 宿主冻结 var,由宿主传入)
|
||||
func set_portrait_texture(tex: Texture2D, flip: bool, box: Vector2) -> void:
|
||||
if tex == null:
|
||||
if _portrait_sprite != null:
|
||||
@@ -56,14 +66,25 @@ func set_portrait_texture(tex: Texture2D, flip: bool, box: Vector2) -> void:
|
||||
|
||||
|
||||
func _place_portrait(box: Vector2) -> void:
|
||||
# 立绘盒 = (0,44)-(box.x, box.y-36);48 源 ×2 整数档居中
|
||||
# 立绘盒 = (0,44)-(box.x, box.y-36);显示档逐键取 PORTRAIT_DISPLAY_H 绝对高,
|
||||
# 宽随源比例(高定宽随);高于盒者沿既有居中溢出机制((box_h-h)*0.5 为负即上溢)
|
||||
if _portrait_sprite == null or _portrait_tex == null:
|
||||
return
|
||||
var eid: String = _owner.enemy_id()
|
||||
var key := "hero" if eid.is_empty() else eid
|
||||
var box_h := box.y - 44.0 - 36.0
|
||||
var w := _portrait_tex.get_width() * 2.0
|
||||
var h := _portrait_tex.get_height() * 2.0
|
||||
_portrait_sprite.scale = Vector2(2.0, 2.0) # 整数 2×(C5)
|
||||
_portrait_sprite.position = Vector2((box.x - w) * 0.5, 44.0 + (box_h - h) * 0.5)
|
||||
var disp_h: float = PORTRAIT_DISPLAY_H.get(key, -1.0)
|
||||
if disp_h < 0.0:
|
||||
# 缺键不崩、不静默兜档——push_error 报警后按 1× 落位(V18 键集自检在测试期报警)
|
||||
push_error("[站桩] 显示档表缺键: %s(逐键登记纪律)" % key)
|
||||
_portrait_sprite.scale = Vector2.ONE
|
||||
_portrait_sprite.position = Vector2((box.x - _portrait_tex.get_width()) * 0.5,
|
||||
44.0 + (box_h - _portrait_tex.get_height()) * 0.5)
|
||||
return
|
||||
var s := disp_h / float(_portrait_tex.get_height())
|
||||
_portrait_sprite.scale = Vector2(s, s)
|
||||
_portrait_sprite.position = Vector2((box.x - _portrait_tex.get_width() * s) * 0.5,
|
||||
44.0 + (box_h - disp_h) * 0.5)
|
||||
|
||||
|
||||
# 组合点注册(battle_stage_view _init——UiKit 常量 load_tex,缺位 null 不注册=该态保持现纹理)
|
||||
|
||||
@@ -147,11 +147,11 @@ func _ready() -> void:
|
||||
problems.append("普攻应零表示(意图指示应隐藏)")
|
||||
# 站桩纹理非空断言(八敌真图落位;缺位回退链保持的对照面)
|
||||
if not bool(standee.portrait_probe().get("has_tex", false)):
|
||||
problems.append("敌立绘纹理未落位(素材管线 48px 档——迭代 6 T2;缺位回退链不应在此态触发)")
|
||||
problems.append("敌立绘纹理未落位(原稿分辨率入包——显示档批;缺位回退链不应在此态触发)")
|
||||
if main.ui.battle.stage.hero_standee == null or not main.ui.battle.stage.hero_standee.has_world_hp_bar:
|
||||
problems.append("B 区主角站桩缺失(世界血条面缺失)")
|
||||
elif not bool(main.ui.battle.stage.hero_standee.portrait_probe().get("has_tex", false)):
|
||||
problems.append("主角立绘纹理未落位(48px 档——迭代 6 T2/T3)")
|
||||
problems.append("主角立绘纹理未落位(显示档 hero=144——显示档批)")
|
||||
# ---- BUG-0001 回归(tooltip 入树后 set_effects 槽位不错位——结算软锁修复沉淀)----
|
||||
var slots_row = main.ui.battle.stage.hero_standee.slot_row # BUG-0001 回归随组件迁入站桩(tooltip/槽位错位同源)
|
||||
var probe_eff: Array = [{"effect_id": "attack_up", "display_name": "增伤", "layers": 1,
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
# V10 名字行提档 FS_HINT 14(探针 name_font_size 实读 + 两绘制点源码锚 + FS_CAPTION 计数锚==1);
|
||||
# V11 退役常量不在册 + 掩尘 BRICK 逐位 + 顶缘线/口沿高光两绘制点改指源码锚;
|
||||
# V12 PLAZA_EDGE 逐位 #4C4038 + 回退描边改指源码锚。
|
||||
# V13–V18 立绘显示档(2026-11-01 迭代 48 卡②)——显示档 = 三档绝对高逐键钉值(96/144/188)+
|
||||
# 入包件 = 原稿紧裁零降采样(源高 == 原稿表值)+纵横倍率恒等+主角三态显示 144+
|
||||
# 跨语言显示档表逐键对账+键集恰九键(§8.2 显示档定值单点)。
|
||||
# headless 无渲染面:像素采样以常量单点 + 源码锚 + 探针几何承载(观测面注记,验收对照表 V5 行)。
|
||||
# 运行:godot --headless --path game --script res://tests/unit/presentation/standee_present_values_test.gd(expect 0)
|
||||
extends SceneTree
|
||||
@@ -110,4 +113,94 @@ func _init() -> void:
|
||||
else "护甲三枚入包(24×24)〔样张批 V9〕",
|
||||
tex9 != null and tex9.get_width() == 24 and tex9.get_height() == 24)
|
||||
|
||||
# ---- V13–V18 立绘显示档(迭代 48 卡②) ----
|
||||
_check_display_tier()
|
||||
|
||||
quit(Kit.finish("standee_present_values"))
|
||||
|
||||
|
||||
# ---- V13–V18 立绘显示档(迭代 48 卡②——显示档 = 三档绝对高,入包件 = 原稿紧裁零降采样) ----
|
||||
|
||||
# 规格钉值(显示档定值单点 = art-bible §8.2;源高 = 原稿表值——旧降采样稿必红;
|
||||
# 显示宽 = round(源宽×档高/源高),食人魔酋长 157 为已接受超宽项):
|
||||
# 键 → [源高, 显示档高, 显示宽]
|
||||
const _DISPLAY_SPEC := {
|
||||
"goblin": [90, 96, 95], "giant_spider": [81, 96, 121], "duergar": [126, 96, 71],
|
||||
"hook_horror": [94, 144, 113], "bugbear": [123, 144, 119], "hero": [92, 144, 138],
|
||||
"ogre_chief": [189, 188, 157], "drider": [189, 188, 139], "duergar_king": [189, 188, 132],
|
||||
}
|
||||
|
||||
|
||||
func _enemy_tex_path(key: String) -> String:
|
||||
return "res://assets/art/character/character_enemy_%s.png" % key
|
||||
|
||||
|
||||
func _check_display_tier() -> void:
|
||||
# V13/V15 九键逐一:真实入口 StandeeBody → set_data(enemy_id) → set_portrait_texture(真纹理) → portrait_probe
|
||||
for key: String in _DISPLAY_SPEC:
|
||||
var spec: Array = _DISPLAY_SPEC[key]
|
||||
var body: Node2D = StandeeBodyScript.new("hero" if key == "hero" else "enemy")
|
||||
root.add_child(body)
|
||||
var tex: Texture2D = UiKit.load_tex(UiKit.TEX_HERO_PORTRAIT if key == "hero" else _enemy_tex_path(key))
|
||||
if key == "hero":
|
||||
body.set_portrait_texture(tex, true)
|
||||
else:
|
||||
body.set_data({"display_name": key, "enemy_id": key, "hp": 3, "max_hp": 3,
|
||||
"order": 1, "armor_type": "light"})
|
||||
body.set_portrait_texture(tex, false)
|
||||
var pp: Dictionary = body.portrait_probe()
|
||||
Kit.check("显示档 %s:源高 %d(原稿表值——旧降采样稿必红)+显示高 %d 逐键钉值〔立绘显示档 V13〕"
|
||||
% [key, int(spec[0]), int(spec[1])],
|
||||
tex != null and int(tex.get_height()) == int(spec[0])
|
||||
and is_equal_approx(float(pp.display_size.y), float(spec[1]))
|
||||
and is_equal_approx(float(pp.display_scale) * float(tex.get_height()), float(spec[1])))
|
||||
Kit.check("显示档 %s:纵横倍率恒等(双轴同倍率)+显示宽随源比例(round 档 %d)〔立绘显示档 V15〕"
|
||||
% [key, int(spec[2])],
|
||||
is_equal_approx(float(pp.display_size.x), float(tex.get_width()) * float(pp.display_scale))
|
||||
and is_equal_approx(float(pp.display_size.y), float(tex.get_height()) * float(pp.display_scale))
|
||||
and absf(float(pp.display_size.x) - float(spec[2])) < 0.51)
|
||||
body.queue_free()
|
||||
# V14 静态源锚:显示档表读表单点 + 零硬编码 2× 倍率
|
||||
var motion_src := _read_source("res://presentation/views/standee_motion.gd")
|
||||
Kit.check("显示档表单点在位(_place_portrait 读 PORTRAIT_DISPLAY_H)且零 Vector2(2.0, 2.0) 字面倍率〔立绘显示档 V14〕",
|
||||
motion_src.contains("PORTRAIT_DISPLAY_H") and not motion_src.contains("Vector2(2.0, 2.0)"))
|
||||
# V16 主角三态:组合根装载(hero_tex 注册链)→ 逐态 apply_hero_state 复读探针
|
||||
var stage_view: Node = load("res://presentation/views/battle_stage_view.gd").new(null, load("res://data/tables/battle_layout.tres"))
|
||||
root.add_child(stage_view)
|
||||
var hs: Node2D = stage_view.hero_standee
|
||||
var pp0: Dictionary = hs.portrait_probe()
|
||||
Kit.check("主角 normal 态:源高 ≥92 且显示高 144(组合根真实装载链)〔立绘显示档 V16〕",
|
||||
bool(pp0.has_tex) and is_equal_approx(float(pp0.display_size.y), 144.0))
|
||||
for hstate: String in ["attack", "hit"]:
|
||||
hs.set_hero_state(hstate)
|
||||
var pph: Dictionary = hs.portrait_probe()
|
||||
Kit.check("主角 %s 态:源高 ≥92 且显示高 144(三态同档)〔立绘显示档 V16〕" % hstate,
|
||||
bool(pph.has_tex) and is_equal_approx(float(pph.display_size.y), 144.0))
|
||||
stage_view.queue_free()
|
||||
# V17 跨语言对账:Python 显示档表(STANDEE_DISPLAY_H 八敌 + STANDEE_SLOT_H 主角)逐键 == GDScript 表
|
||||
# (读不到工具源 = L2 记录退回双端字面钉值+独立审计对账——禁当红亦禁当绿)
|
||||
var py_src := ""
|
||||
for py_path: String in ["../tools/pixelize_candidates.py", "tools/pixelize_candidates.py"]:
|
||||
var pf := FileAccess.open(py_path, FileAccess.READ)
|
||||
if pf != null:
|
||||
py_src = pf.get_as_text()
|
||||
break
|
||||
if py_src.is_empty():
|
||||
print("[L2] V17 工具源不可读(../tools/ 路径不通)——退回双端字面钉值+独立审计对账(记录不判红)")
|
||||
else:
|
||||
var py_ok := true
|
||||
for key: String in _DISPLAY_SPEC:
|
||||
if key == "hero":
|
||||
py_ok = py_ok and py_src.contains("STANDEE_SLOT_H = %d" % int(_DISPLAY_SPEC[key][1]))
|
||||
else:
|
||||
py_ok = py_ok and py_src.contains("\"%s\": %d" % [key, int(_DISPLAY_SPEC[key][1])])
|
||||
Kit.check("Python 显示档表与 GDScript PORTRAIT_DISPLAY_H 九键逐键相等(源文本对账)〔立绘显示档 V17〕", py_ok)
|
||||
# V18 键集恰九键(八敌 ∪ hero,无多余键——防新敌种漏登记被 push_error 静默)
|
||||
var motion_script: Script = load("res://presentation/views/standee_motion.gd")
|
||||
var mcmap: Dictionary = motion_script.get_script_constant_map()
|
||||
var disp_tbl: Dictionary = mcmap.get("PORTRAIT_DISPLAY_H", {})
|
||||
var keys_ok: bool = disp_tbl.size() == 9
|
||||
for k: String in ["bugbear", "drider", "duergar", "duergar_king", "giant_spider",
|
||||
"goblin", "hook_horror", "ogre_chief", "hero"]:
|
||||
keys_ok = keys_ok and disp_tbl.has(k)
|
||||
Kit.check("显示档表键集 = 八敌 ∪ hero 恰九键(无多余键)〔立绘显示档 V18〕", keys_ok)
|
||||
+1
-1
@@ -44,7 +44,7 @@ ACTIVE_FILES = [
|
||||
"fx/shovel_swing_f2.png",
|
||||
"board/seal_mark.png",
|
||||
]
|
||||
# 立绘八敌按数据表键生成(键集=STANDEE_TIER_H,见 standee_asset_check)
|
||||
# 立绘八敌按数据表键生成(键集=STANDEE_DISPLAY_H,见 standee_asset_check)
|
||||
ENEMY_KEYS = ("bugbear", "drider", "duergar", "duergar_king", "giant_spider",
|
||||
"goblin", "hook_horror", "ogre_chief")
|
||||
ACTIVE_FILES += ["character/character_enemy_%s.png" % k for k in ENEMY_KEYS]
|
||||
|
||||
@@ -12,9 +12,9 @@ v3 设计要点(对应四类问题:脏点/模糊/信息丢失/白边):
|
||||
用法:
|
||||
python tools/pixelize_candidates.py <候选批次目录> # v3 批量像素化(现行行为)
|
||||
python tools/pixelize_candidates.py <目录> --fixer # +rust fixer 收网格+封闭灰袋清除
|
||||
python tools/pixelize_candidates.py <目录> --standee48 [--standee-out <game/assets/art/character>]
|
||||
# 九张 48px 站桩槽位档(八敌 pixel-1+主角 normal;
|
||||
# 底中锚/NEAREST/统一比例保体量/画布高恒 48)
|
||||
python tools/pixelize_candidates.py <目录> --standee-display [--standee-out <game/assets/art/character>]
|
||||
# 九张站桩原稿分辨率稿(八敌 pixel-1+主角手修 2x 稿;
|
||||
# 内容紧裁原样存盘,零降采样——显示缩放归游戏侧按档)
|
||||
--fixer: rust pixelfixer(Retro-Diffusion,
|
||||
MIT, D:/code/pixel-art-fixer)对原图先做网格检测+重建(收网格),v3 去底后再跑封闭灰袋清除
|
||||
(三闸=近底色∧中性灰∧组件不触图缘——防误伤盔甲与影);--fixer-exe 可换引擎路径。
|
||||
@@ -25,25 +25,29 @@ from PIL import Image, ImageFilter
|
||||
|
||||
FIXER_EXE_DEFAULT = r"D:\code\pixel-art-fixer\rust\target\release\pixelfixer.exe"
|
||||
FIXER_EXE = FIXER_EXE_DEFAULT # --fixer-exe 可覆写(__main__ 解析)
|
||||
STANDEE_SLOT_H = 48 # 主角站桩源档高(×2 整数放大=盒高 96——圣经 §8.2)
|
||||
STANDEE_SLOT_W_MAX = 75 # 盒宽 150 的一半(超宽才按宽收,现八敌+主角均不触)
|
||||
STANDEE_SLOT_H = 144 # 主角显示档高(中型档——art-bible §8.2 三档绝对高)
|
||||
|
||||
# 八敌站桩源档高 = 生物体型,不是强度档位
|
||||
# 口径:体量按「这只生物本来多大」定;强度改由别的通道承载(Boss 的紫赤色相独占、
|
||||
# 精英的垂直延伸、HUD 意图徽标)。因此下表与 §5.5 的普通/精英/Boss 档位**不对齐**——
|
||||
# 例如钩爪恐魔是普通怪却比精英灰矮人大,灰矮人是精英却比普通/精英熊地精小。
|
||||
# 数值经实测校准(视觉体量 = 不透明像素面积,不看高度——蜘蛛宽扁,按高度排会失真):
|
||||
# 地精 828 < 灰矮人 1014 < 巨蜘蛛 1390 < 钩爪恐魔 1538
|
||||
# < 熊地精 2145 < 食人魔酋长 2886 < 蛛化卓尔精灵 3513 < 灰矮人石君王 4385
|
||||
STANDEE_TIER_H = {
|
||||
"goblin": 36, "giant_spider": 38,
|
||||
"duergar": 46, "hook_horror": 58,
|
||||
"bugbear": 62, "ogre_chief": 74,
|
||||
"drider": 90, "duergar_king": 94,
|
||||
# 八敌站桩显示档 = 三档绝对高(art-bible §8.2 显示档定值单点)——小型 96 / 中型 144 / 大型 188
|
||||
# 归档 = 生物体型语义(character-design §5.2):小 < 中 < 大,禁按强度重排。
|
||||
# 显示倍率 = 档高 / 原稿高,宽随源比例(高定宽随,NEAREST);入包件 = 原稿紧裁原样,
|
||||
# 禁任何降采样(2026-10-14 降档裁决作废——现行入包稿与原稿逐像素同)。
|
||||
STANDEE_DISPLAY_H = {
|
||||
"goblin": 96, "giant_spider": 96, "duergar": 96,
|
||||
"hook_horror": 144, "bugbear": 144,
|
||||
"ogre_chief": 188, "drider": 188, "duergar_king": 188,
|
||||
}
|
||||
|
||||
# 原稿高表(入包件逐件源高 == 此值——禁降采样纪律的机检锚;主角 = 手修 2x 稿紧裁后高)
|
||||
STANDEE_SRC_H = {
|
||||
"goblin": 90, "giant_spider": 81, "duergar": 126,
|
||||
"hook_horror": 94, "bugbear": 123,
|
||||
"ogre_chief": 189, "drider": 189, "duergar_king": 189,
|
||||
"hero": 92,
|
||||
}
|
||||
|
||||
# 中间产物(pixel-1)的生成分辨率——只决定中间图有多细,**不决定最终体量**。
|
||||
# 最终体量归 STANDEE_TIER_H。这里取得比最终档高越大,后续降采样越干净。
|
||||
# 站桩入包件 = 原稿紧裁原样(STANDEE_SRC_H),显示体量归 STANDEE_DISPLAY_H。这里取得
|
||||
# 比显示档越大,入包件细节越足。
|
||||
# 注意:ogre_chief 取 192 是分辨率选择(要给它更高的中间精度),不是把它归为 Boss 档。
|
||||
SIZES = {"goblin": 96, "giant_spider": 96, "hook_horror": 96,
|
||||
"bugbear": 128, "duergar": 128,
|
||||
@@ -456,60 +460,62 @@ def dom_block_resize(im, nw, nh):
|
||||
return out
|
||||
|
||||
|
||||
def build_standee48(batch_root, out_dir, extra_hero_states=("attack", "hit")):
|
||||
"""出八敌 + 主角的站桩源档。源 = 八敌 pixel-1 + 主角 normal_1x(追加 attack/hit,默认启用)。
|
||||
def build_standee_display(batch_root, out_dir, extra_hero_states=("attack", "hit")):
|
||||
"""出八敌 + 主角的站桩入包稿(原稿分辨率,零降采样)。源 = 八敌 pixel-1 + 主角手修 2x 稿。
|
||||
|
||||
口径:
|
||||
1. 八敌各自按 STANDEE_TIER_H 定高——那是生物体型,不是强度档位。
|
||||
(实测可读门槛约 32px,48px 舒适;若全敌共用「48/最大值」一个系数,
|
||||
普通怪被压到 21–24px 低于门槛、钩爪恐魔糊成一团——禁用统一比例。)
|
||||
2. 主角仍按 STANDEE_SLOT_H(48)定高。
|
||||
3. 画布高度 = 该角色自己的档高;内容裁透明边后底中锚贴底缘。
|
||||
4. 降采样走 dom_block_resize(块内众数投票,零插值)。
|
||||
1. 内容紧裁透明边后原样存盘——画布即内容,禁任何缩放与降采样(art-bible §8.2:
|
||||
显示按三档绝对高在游戏侧缩放,入包件保留原稿分辨率)。
|
||||
2. 逐件自证:紧裁后高度 == STANDEE_SRC_H 表值;九件计数 != 9 即停
|
||||
(缺件即停——禁回退旧降采样稿顶替)。
|
||||
"""
|
||||
os.makedirs(out_dir, exist_ok=True)
|
||||
enemy_srcs = {}
|
||||
for key in sorted(SIZES.keys()):
|
||||
for key in sorted(STANDEE_SRC_H.keys()):
|
||||
if key == "hero":
|
||||
continue
|
||||
p = os.path.join(batch_root, key, "pixel-1.png")
|
||||
if os.path.isfile(p):
|
||||
enemy_srcs[key] = Image.open(p).convert("RGBA")
|
||||
hero_p = os.path.join(batch_root, "hero-ref", "edit", "normal_1x.png")
|
||||
hero_p = os.path.join(batch_root, "hero-ref", "edit", "normal_2x.png")
|
||||
jobs = [(k, im, "character_enemy_%s.png" % k) for k, im in enemy_srcs.items()]
|
||||
if os.path.isfile(hero_p):
|
||||
jobs.append(("hero_normal", Image.open(hero_p).convert("RGBA"), "character_hero_portrait.png"))
|
||||
for st in extra_hero_states:
|
||||
sp = os.path.join(batch_root, "hero-ref", "edit", "%s_1x.png" % st)
|
||||
sp = os.path.join(batch_root, "hero-ref", "edit", "%s_2x.png" % st)
|
||||
if os.path.isfile(sp):
|
||||
jobs.append(("hero_%s" % st, Image.open(sp).convert("RGBA"),
|
||||
"character_hero_%s.png" % st))
|
||||
made = []
|
||||
n_pieces = 0 # 九件 = 八敌 + 主角立绘(attack/hit 为立绘伴随态,不入计数)
|
||||
for key, im, out_name in jobs:
|
||||
l, t, r, b = _bbox_alpha(im)
|
||||
im = im.crop((l, t, r, b))
|
||||
is_hero = key == "hero_normal" or key.startswith("hero_")
|
||||
slot_h = STANDEE_SLOT_H if is_hero else STANDEE_TIER_H.get(key, STANDEE_SLOT_H)
|
||||
f = slot_h / float(im.height)
|
||||
nw, nh = max(1, int(round(im.width * f))), max(1, int(round(im.height * f)))
|
||||
if nw > STANDEE_SLOT_W_MAX: # 超宽兜底(现无触)——按宽收再贴底
|
||||
k2 = STANDEE_SLOT_W_MAX / float(nw)
|
||||
nw, nh = STANDEE_SLOT_W_MAX, max(1, int(round(nh * k2)))
|
||||
body = dom_block_resize(im, nw, min(nh, slot_h))
|
||||
canvas = Image.new("RGBA", (nw, slot_h), (0, 0, 0, 0))
|
||||
canvas.alpha_composite(body, (0, slot_h - body.height)) # 底中锚:脚贴画布底缘
|
||||
src_key = "hero" if key.startswith("hero") else key
|
||||
if im.height != STANDEE_SRC_H.get(src_key):
|
||||
print("FAIL 站桩原稿高失配 %s: 实得 %d, 表值 %s(禁降采样——查源稿)"
|
||||
% (key, im.height, STANDEE_SRC_H.get(src_key)))
|
||||
sys.exit(2)
|
||||
outp = os.path.join(out_dir, out_name)
|
||||
canvas.save(outp)
|
||||
print("standee %-14s %sx%s -> %s" % (key, nw, slot_h, outp))
|
||||
im.save(outp)
|
||||
print("standee %-14s %dx%d -> %s" % (key, im.width, im.height, outp))
|
||||
made.append(outp)
|
||||
if out_name.startswith("character_enemy_") or out_name == "character_hero_portrait.png":
|
||||
n_pieces += 1
|
||||
if n_pieces != len(STANDEE_SRC_H):
|
||||
print("FAIL 站桩产物计数 %d != %d(缺件即停——禁回退旧降采样稿顶替)"
|
||||
% (n_pieces, len(STANDEE_SRC_H)))
|
||||
sys.exit(2)
|
||||
return made
|
||||
|
||||
|
||||
def main(argv):
|
||||
root = argv[0] if argv and not argv[0].startswith("--") else "artwork/remote-gen-2026-09-26"
|
||||
args = argv[1:] if argv and not argv[0].startswith("--") else argv
|
||||
if "--standee48" in args:
|
||||
if "--standee-display" in args:
|
||||
out = args[args.index("--standee-out") + 1] if "--standee-out" in args \
|
||||
else os.path.join("game", "assets", "art", "character")
|
||||
build_standee48(root, out)
|
||||
build_standee_display(root, out)
|
||||
return
|
||||
use_fixer = "--fixer" in args
|
||||
thumbs = []
|
||||
|
||||
@@ -51,6 +51,7 @@ import hashlib
|
||||
from collections import deque
|
||||
|
||||
from layout_config import load as load_layout
|
||||
from pixelize_candidates import STANDEE_SLOT_H # 站桩显示档单点(主角=中型档——art-bible §8.2)
|
||||
|
||||
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
MAIN_ROOT = "D:/code/g-plan2"
|
||||
@@ -772,7 +773,7 @@ def _armor_tex(armor):
|
||||
|
||||
def draw_standee(canvas, ox, oy, kind, name, enemy_id, order=1, armor="light",
|
||||
hp=1.0, intent="", statuses=(), dead=False):
|
||||
"""站桩 150×176(standee_body.gd 几何):血条+状态行头顶/立绘盒 96px/底座/敌名行。"""
|
||||
"""站桩 150×176(standee_body.gd 几何):血条+状态行头顶/立绘盒 96px 槽位(显示档居中溢出)/底座/敌名行。"""
|
||||
d = ImageDraw.Draw(canvas)
|
||||
W, H = 150, 176
|
||||
# 头顶:顺位徽标(敌,FS_HINT 14)+血条(填充=危险红 #A8321F 不因敌我分色;条底=HP_BAR_TRACK)
|
||||
@@ -788,11 +789,13 @@ def draw_standee(canvas, ox, oy, kind, name, enemy_id, order=1, armor="light",
|
||||
for i, ic in enumerate(statuses):
|
||||
icon = STATUS_ICONS[ic].resize((16, 16), Image.NEAREST)
|
||||
canvas.alpha_composite(icon, (sx + i * 17, oy + 18))
|
||||
# 立绘盒 96px(48 源 ×2 整数——C5)
|
||||
# 立绘盒 96px 槽位(hero 显示档 = STANDEE_SLOT_H 绝对高、宽随源比例——§8.2,居中溢出同 _place_portrait)
|
||||
pbox_y = oy + 44
|
||||
if kind == "hero":
|
||||
por = PORTRAIT_SRC.resize((96, 96), Image.NEAREST)
|
||||
canvas.alpha_composite(por, (ox + (W - 96) // 2, pbox_y))
|
||||
disp_h = STANDEE_SLOT_H
|
||||
disp_w = int(round(PORTRAIT_SRC.width * disp_h / float(PORTRAIT_SRC.height)))
|
||||
por = PORTRAIT_SRC.resize((disp_w, disp_h), Image.NEAREST)
|
||||
canvas.alpha_composite(por, (ox + (W - disp_w) // 2, pbox_y + (96 - disp_h) // 2))
|
||||
else:
|
||||
glyph = {"goblin": "▂", "bugbear": "▆", "ogre_chief": "█"}.get(enemy_id, "敌")
|
||||
col = (150, 60, 48) if not dead else (110, 104, 98)
|
||||
@@ -1131,11 +1134,11 @@ def run_checks(board, placement, records, band, gmap, canvas_img=None):
|
||||
edge_hits = edge_content(canvas_img)
|
||||
check("A5", "画布四缘 2px 净空(零溢出)", edge_hits == 0, "缘带内容像素 %d" % edge_hits)
|
||||
# A4 混排相融:小人/立绘/状态图标=在库原件;语义色==UiKit 常量
|
||||
# A4 尺寸基线按现行资产实态固化——立绘 46×48/棋盘小人源 32×32
|
||||
# A4 尺寸基线按现行资产实态固化——立绘 88×92(原稿分辨率)/棋盘小人源 32×32
|
||||
# 硬等断言落锁(资产升档换代须显式解冻重校,断言失败=预期闸)。
|
||||
check("A4", "混排相融(在库原件可载+UiKit 语义色对表+尺寸基线冻结:立绘 46×48/小人源 32×32)",
|
||||
PORTRAIT_SRC.size == (46, 48) and HERO_SRC.size == (32, 32),
|
||||
"小人源 %s(裁透明留白后 %s)/立绘 %s[冻结 46×48]/状态图标原件;INK/封印黑/奖励金 == ui_kit.gd"
|
||||
check("A4", "混排相融(在库原件可载+UiKit 语义色对表+尺寸基线冻结:立绘 88×92/小人源 32×32)",
|
||||
PORTRAIT_SRC.size == (88, 92) and HERO_SRC.size == (32, 32),
|
||||
"小人源 %s(裁透明留白后 %s)/立绘 %s[冻结 88×92]/状态图标原件;INK/封印黑/奖励金 == ui_kit.gd"
|
||||
% (str(HERO_SRC.size), str(HERO.size), str(PORTRAIT_SRC.size)))
|
||||
return all(c[2] for c in CHECKS)
|
||||
|
||||
@@ -1497,42 +1500,42 @@ def render_outline_overlap():
|
||||
img = Image.new("RGBA", (W, H), (24, 20, 17, 255))
|
||||
d = ImageDraw.Draw(img)
|
||||
meta = {"hero_boxes": [], "overlap": None, "armor": {}}
|
||||
# ① 战斗舞台语境:主角立绘 96px + 深墨描边
|
||||
por = PORTRAIT_SRC.resize((96, 96), Image.NEAREST)
|
||||
# ① 战斗舞台语境:主角立绘显示档(STANDEE_SLOT_H 绝对高、宽随源比例)+深墨描边
|
||||
por = PORTRAIT_SRC.resize((int(round(PORTRAIT_SRC.width * STANDEE_SLOT_H / float(PORTRAIT_SRC.height))), STANDEE_SLOT_H), Image.NEAREST)
|
||||
img.alpha_composite(_outline_underlay(por, (40, 19, 13)), (18, 14))
|
||||
img.alpha_composite(por, (18, 14))
|
||||
meta["hero_boxes"].append((18, 14, 18 + 96, 14 + 96))
|
||||
d.text((18, 116), "主角·战斗舞台语境\n描边=深墨 (40,19,13)", font=F_LBL2, fill=(225, 215, 195))
|
||||
meta["hero_boxes"].append((18, 14, 18 + por.size[0], 14 + por.size[1]))
|
||||
d.text((18, 166), "主角·战斗舞台语境 描边=深墨 (40,19,13)", font=F_LBL2, fill=(225, 215, 195))
|
||||
# ② 棋盘格语境:小人 48px 压已开格底
|
||||
d.rectangle([150, 14, 214, 78], fill=(24, 20, 16))
|
||||
d.rectangle([210, 14, 274, 78], fill=(24, 20, 16))
|
||||
fl = FLOORS[0].resize((64, 64), Image.NEAREST)
|
||||
img.paste(fl.convert("RGB"), (150, 14))
|
||||
img.paste(fl.convert("RGB"), (210, 14))
|
||||
h48 = HERO.resize((48, int(48 * HERO.size[1] / HERO.size[0])), Image.NEAREST)
|
||||
img.alpha_composite(h48, (150 + (64 - h48.size[0]) // 2, 14 + 64 - h48.size[1] - 8))
|
||||
meta["hero_boxes"].append((150 + (64 - h48.size[0]) // 2, 14 + 64 - h48.size[1] - 8,
|
||||
150 + (64 - h48.size[0]) // 2 + h48.size[0], 14 + 64 - 8))
|
||||
d.text((150, 116), "棋盘格语境\n(隔离已开格底)", font=F_LBL2, fill=(225, 215, 195))
|
||||
img.alpha_composite(h48, (210 + (64 - h48.size[0]) // 2, 14 + 64 - h48.size[1] - 8))
|
||||
meta["hero_boxes"].append((210 + (64 - h48.size[0]) // 2, 14 + 64 - h48.size[1] - 8,
|
||||
210 + (64 - h48.size[0]) // 2 + h48.size[0], 14 + 64 - 8))
|
||||
d.text((210, 116), "棋盘格语境\n(隔离已开格底)", font=F_LBL2, fill=(225, 215, 195))
|
||||
# ③ 结算明细带语境:小人 24px 压明细带底
|
||||
d.rectangle([238, 14, 302, 78], fill=(46, 39, 32))
|
||||
d.rectangle([298, 14, 362, 78], fill=(46, 39, 32))
|
||||
h24 = HERO.resize((24, int(24 * HERO.size[1] / HERO.size[0])), Image.NEAREST)
|
||||
img.alpha_composite(h24, (238 + 8, 14 + 52 - h24.size[1]))
|
||||
meta["hero_boxes"].append((238 + 8, 14 + 52 - h24.size[1], 238 + 8 + h24.size[0], 14 + 52))
|
||||
d.text((238, 116), "结算明细带语境\n(同剪影 24px)", font=F_LBL2, fill=(225, 215, 195))
|
||||
img.alpha_composite(h24, (298 + 8, 14 + 52 - h24.size[1]))
|
||||
meta["hero_boxes"].append((298 + 8, 14 + 52 - h24.size[1], 298 + 8 + h24.size[0], 14 + 52))
|
||||
d.text((298, 116), "结算明细带语境\n(同剪影 24px)", font=F_LBL2, fill=(225, 215, 195))
|
||||
# ④ 敌方描边对照:字形多向偏移暖白垫底+本体深朱
|
||||
for ddx, ddy in [(-1, 0), (1, 0), (0, -1), (0, 1), (-1, -1), (1, -1), (-1, 1), (1, 1)]:
|
||||
d.text((350 + 32 + ddx, 14 + 32 + ddy), "█", font=F_STANDEE, fill=(240, 226, 200), anchor="mm")
|
||||
d.text((350 + 32, 14 + 32), "█", font=F_STANDEE, fill=(150, 60, 48), anchor="mm")
|
||||
d.text((350, 116), "敌方描边=暖白族\n(非黑非深褐)", font=F_LBL2, fill=(225, 215, 195))
|
||||
d.text((410 + 32 + ddx, 14 + 32 + ddy), "█", font=F_STANDEE, fill=(240, 226, 200), anchor="mm")
|
||||
d.text((410 + 32, 14 + 32), "█", font=F_STANDEE, fill=(150, 60, 48), anchor="mm")
|
||||
d.text((410, 116), "敌方描边=暖白族\n(非黑非深褐)", font=F_LBL2, fill=(225, 215, 195))
|
||||
# ⑤ 护甲三枚展示(源 24×24 挂 HP_BAR_BACK 素底——工单 §5.3 挂载底色口径)
|
||||
for i, key in enumerate(["light", "ward", "heavy"]):
|
||||
ax = 470 + i * 60
|
||||
ax = 500 + i * 60
|
||||
d.rectangle([ax, 14, ax + 40, 54], fill=(40, 34, 32))
|
||||
tex = _armor_tex(key)
|
||||
if tex is not None:
|
||||
img.paste(tex, (ax + 8, 22), tex)
|
||||
meta["armor"][key] = (ax + 8, 22, ax + 8 + 24, 22 + 24)
|
||||
d.text((ax + 4, 58), {"light": "轻甲", "ward": "魔甲", "heavy": "重甲"}[key], font=F_LBL2, fill=(225, 215, 195))
|
||||
d.text((470, 116), "护甲三枚 24×24\n挂载底=名字行素底", font=F_LBL2, fill=(225, 215, 195))
|
||||
d.text((500, 116), "护甲三枚 24×24\n挂载底=名字行素底", font=F_LBL2, fill=(225, 215, 195))
|
||||
# ⑥ 遮挡对照组:敌像(最底)→旗→小人+蓝框→数字(最顶)——P5 断言:顶层三件仍可辨
|
||||
cx, cy0 = 700, 10
|
||||
d.rectangle([cx, cy0, cx + 68, cy0 + 68], fill=(24, 20, 16))
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""站桩素材机检(B1 硬闸升级)。
|
||||
"""站桩素材机检(B1 硬闸,显示档口径)。
|
||||
|
||||
升级依据=art-bible §8.3③ 挂点规格(审计 §07-2/3):
|
||||
- 逐键校验档高(逐键断言 STANDEE_TIER_H),宽度按「源宽×2 ≤ 站桩盒宽 150」
|
||||
物理约束校验;
|
||||
判据(art-bible §8.2 站桩立绘显示档 = 三档绝对高):
|
||||
- 逐键校验源高(逐件断言 STANDEE_SRC_H——入包件 = 原稿紧裁原样,禁降采样);
|
||||
- 显示宽按「round(源宽 × 档高 / 源高) ≤ 站桩盒宽 150」校验
|
||||
(食人魔酋长 157 超宽 8px 为已接受项,登记例外表逐字校验);
|
||||
- 像素域色数 ≤32(**排除全透明像素**,见下方口径注);
|
||||
- 无非整数倍缩放痕迹(alpha 二值 + 无中间 α);
|
||||
- 描边亮度达标(亮描边暖白族,character-design §10硬规则「禁黑描边」)。
|
||||
@@ -13,8 +14,9 @@
|
||||
naive `len(set(所有像素))` 会把全透明 (0,0,0,0) 计成一个色 ⇒ 报33 假红灯。
|
||||
正确口径 = 只统计 alpha>0 像素的唯一 RGB。机检必须如此,否则天天误报。
|
||||
|
||||
档高表= tools/pixelize_candidates.py 的 STANDEE_TIER_H(定值单点纪律 §8:本表
|
||||
不在检查器里复写一份,改由 import 读,避免两处数字漂移)。
|
||||
表单点 = tools/pixelize_candidates.py 的 STANDEE_DISPLAY_H / STANDEE_SRC_H /
|
||||
STANDEE_SLOT_H(定值单点纪律 §8:本件不在检查器里复写一份,改由 import 读;
|
||||
import 失败即崩——禁兜底字面量造第三处复写)。
|
||||
|
||||
用法:
|
||||
python tools/standee_asset_check.py [角色素材目录]
|
||||
@@ -29,16 +31,7 @@ import sys
|
||||
from PIL import Image
|
||||
|
||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||
try:
|
||||
from pixelize_candidates import STANDEE_TIER_H, STANDEE_SLOT_H
|
||||
except ImportError: # pragma: no cover
|
||||
STANDEE_TIER_H = {
|
||||
"goblin": 36, "giant_spider": 38,
|
||||
"duergar": 46, "hook_horror": 58,
|
||||
"bugbear": 62, "ogre_chief": 74,
|
||||
"drider": 90, "duergar_king": 94,
|
||||
}
|
||||
STANDEE_SLOT_H = 48
|
||||
from pixelize_candidates import STANDEE_DISPLAY_H, STANDEE_SRC_H, STANDEE_SLOT_H
|
||||
|
||||
ENEMY_KEYS = ("bugbear", "drider", "duergar", "duergar_king", "giant_spider",
|
||||
"goblin", "hook_horror", "ogre_chief")
|
||||
@@ -48,9 +41,10 @@ REQUIRED = ["character_enemy_%s.png" % k for k in ENEMY_KEYS] + \
|
||||
|
||||
# ── 契约常量(全部有出处,禁复写第二份) ──────────────────────────────
|
||||
COLOR_MAX = 32 # 圣经 §4.1/fixer 32 色冻结;机检口径=alpha>0 唯一 RGB
|
||||
DISPLAY_SCALE = 2 # standee_body.gd:237 _portrait_sprite.scale = (2,2)
|
||||
BOX_W = 150 # standee_body.gd:29 var box := Vector2(150, 176)
|
||||
SRC_W_MAX = BOX_W // DISPLAY_SCALE # =75,源宽上限(×2 后须塞进盒宽)
|
||||
# 显示宽例外(已接受项——art-bible §8.2 槽位条):食人魔酋长显示宽 157 超桩盒 150,
|
||||
# 逐字校验 = 实算宽必须恰等于登记值,源稿一变即红。
|
||||
DISPLAY_W_EXCEPT = {"character_enemy_ogre_chief.png": 157}
|
||||
ALPHA_BIN = {0, 255} # 二值 alpha 域
|
||||
# 描边亮度阈值(实测标定):
|
||||
# 敌人暖白边占比实测 54.1%–84.3%,主角 portrait/hit 实测 0.4%(无暖白描边,见下)。
|
||||
@@ -72,12 +66,19 @@ def _sat(c):
|
||||
return 0.0 if mx == 0 else (mx - mn) / float(mx)
|
||||
|
||||
|
||||
def expected_tier_h(fn):
|
||||
"""按文件名返回应有档高。"""
|
||||
def src_key_of(fn):
|
||||
"""按文件名返回 STANDEE_SRC_H 键(敌=键名;主角三态同键 hero)。"""
|
||||
if fn.startswith("character_enemy_") and fn.endswith(".png"):
|
||||
key = fn[len("character_enemy_"):-len(".png")]
|
||||
return STANDEE_TIER_H.get(key)
|
||||
return STANDEE_SLOT_H # 主角三态同档
|
||||
return fn[len("character_enemy_"):-len(".png")]
|
||||
return "hero" # 主角三态同源稿高
|
||||
|
||||
|
||||
def display_h_of(fn):
|
||||
"""按文件名返回显示档高(敌=逐键显示档;主角=中型档 144)。"""
|
||||
key = src_key_of(fn)
|
||||
if key == "hero":
|
||||
return STANDEE_SLOT_H
|
||||
return STANDEE_DISPLAY_H.get(key)
|
||||
|
||||
|
||||
def check(path, fn):
|
||||
@@ -87,15 +88,19 @@ def check(path, fn):
|
||||
w, h = im.size
|
||||
px = im.load()
|
||||
|
||||
# 断言①逐键档高
|
||||
tier = expected_tier_h(fn)
|
||||
if tier is not None and h != tier:
|
||||
problems.append("档高应 %d(§8.2 逐键定档),实得 %d" % (tier, h))
|
||||
# 断言① 逐件源高 = 原稿表值(禁降采样——旧降采样稿必红)
|
||||
src_h = STANDEE_SRC_H.get(src_key_of(fn))
|
||||
if src_h is not None and h != src_h:
|
||||
problems.append("源高应 %d(原稿表值——禁降采样),实得 %d" % (src_h, h))
|
||||
|
||||
# 断言② 宽度物理约束:源宽 ×2 须塞进站桩盒宽 150
|
||||
if w > SRC_W_MAX:
|
||||
problems.append("源宽 %d ×2=%d 超站桩盒宽 %d(§8.2 高定宽随但不得溢出盒)"
|
||||
% (w, w * DISPLAY_SCALE, BOX_W))
|
||||
# 断言② 显示宽物理约束:round(源宽 × 档高 / 源高) ≤ 盒宽 150(酋长 157 例外逐字校验)
|
||||
disp_h = display_h_of(fn)
|
||||
disp_w = -1
|
||||
if disp_h is not None and src_h:
|
||||
disp_w = int(round(w * disp_h / float(src_h)))
|
||||
if disp_w > BOX_W and DISPLAY_W_EXCEPT.get(fn) != disp_w:
|
||||
problems.append("显示宽 %d 超站桩盒宽 %d(§8.2 高定宽随;例外表未登记)"
|
||||
% (disp_w, BOX_W))
|
||||
if w < 1:
|
||||
problems.append("源宽 %d 非法" % w)
|
||||
|
||||
@@ -119,7 +124,7 @@ def check(path, fn):
|
||||
if nb:
|
||||
edges.append(c)
|
||||
|
||||
# 断言③ 底中锚(脚贴画布底缘)
|
||||
# 断言③ 底中锚(脚贴画布底缘——紧裁件底行即脚底)
|
||||
if bottom_opaque < 1:
|
||||
problems.append("底行无不透明像素(底中锚失守——脚未贴画布底缘)")
|
||||
|
||||
@@ -148,7 +153,9 @@ def check(path, fn):
|
||||
|
||||
info = {
|
||||
"size": [w, h],
|
||||
"tier_h": tier,
|
||||
"src_h": src_h,
|
||||
"display_h": disp_h,
|
||||
"display_w": disp_w,
|
||||
"colors": len(colors),
|
||||
"bottom_opaque": bottom_opaque,
|
||||
"edge_px": len(edges),
|
||||
@@ -188,9 +195,10 @@ def main():
|
||||
for pr in r["problems"]:
|
||||
print("FAIL %-36s %s" % (r["file"], pr))
|
||||
else:
|
||||
print("PASS %-36s %dx%d 档高%d 色数%d 暖白边%.1f%%%s"
|
||||
% (r["file"], i["size"][0], i["size"][1], i["tier_h"],
|
||||
i["colors"], i["outline_warm_pct"],
|
||||
print("PASS %-36s %dx%d 源高%d 显示%dx%d 色数%d 暖白边%.1f%%%s"
|
||||
% (r["file"], i["size"][0], i["size"][1], i["src_h"],
|
||||
i["display_w"], i["display_h"], i["colors"],
|
||||
i["outline_warm_pct"],
|
||||
"(豁免)" if i["outline_exempt"] else ""))
|
||||
print("机检完成:%d 件必需,%d 件未过" % (len(REQUIRED), n_fail))
|
||||
if not as_json:
|
||||
@@ -200,4 +208,4 @@ def main():
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
main()
|
||||
Reference in new issue
Block a user