121 lines
5.1 KiB
Python
121 lines
5.1 KiB
Python
#!/usr/bin/env python3
|
||
# -*- coding: utf-8 -*-
|
||
"""UI 全局主题小位图生成脚本。
|
||
|
||
产出(→ game/assets/art/ui/gen/,纯色/几何、像素风):
|
||
slider_grabber.png 滑轨柄·常 12×12 CANDLE #F6C667 + PANEL_BORDER #786E58 1px 描边
|
||
slider_grabber_highlight.png 滑轨柄·悬 12×12 CANDLE 提亮档(lightened≈12%) + 同描边
|
||
slider_grabber_disabled.png 滑轨柄·废 12×12 STONE_DEEP #554D41 + 同描边
|
||
spinbox_updown.png SpinBox 箭头 16×16 INK #33261B ▲▼ 合一(Godot 4 SpinBox 走 updown 合并图标槽)
|
||
option_arrow.png OptionButton 箭头 8×8 INK 向下三角
|
||
checkbox_unchecked.png 勾选框·空 14×14 PANEL_DATA #E9D9B8 + PANEL_BORDER 边
|
||
checkbox_checked.png 勾选框·选 14×14 同底边 + INK 对勾
|
||
checkbox_unchecked_disabled.png 勾选框·空·废 14×14 同底 + STONE_DEEP 边
|
||
checkbox_checked_disabled.png 勾选框·选·废 14×14 同底边 + STONE_DEEP 对勾
|
||
|
||
色值全部取自 game/presentation/views/ui_kit.gd 既有常量(溯源表见 game/assets/theme/README.md)——
|
||
不新增美术素材、不动美术圣经 §7。滚动条柄不走位图:Godot 4 ScrollBar 的
|
||
grabber 是 StyleBox(非 Icon),纯色 StyleBoxFlat 直接承载(见 main_theme.tres)。
|
||
|
||
♻ 可重跑:本脚本幂等,重复执行覆盖产出;改色值只改本脚本顶部常量后重跑 + 重导。
|
||
用法: python tools/gen_theme_icons.py
|
||
"""
|
||
import os
|
||
|
||
from PIL import Image
|
||
|
||
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||
OUT = os.path.join(ROOT, "game", "assets", "art", "ui", "gen")
|
||
|
||
# ── 色值(↔ ui_kit.gd 常量,冻结)────────────────────────────────────────────
|
||
INK = (51, 38, 27) # INK #33261B
|
||
PANEL_DATA = (233, 217, 184) # PANEL_DATA #E9D9B8
|
||
PANEL_BORDER = (120, 110, 88) # PANEL_BORDER #786E58
|
||
STONE_DEEP = (85, 77, 65) # STONE_DEEP #554D41
|
||
CANDLE = (246, 198, 103) # CANDLE #F6C667
|
||
|
||
|
||
def lighten(c, amount=0.12):
|
||
"""复刻 Godot Color.lightened:每通道 + (255-通道)×amount(sRGB 存储值域)"""
|
||
return tuple(min(255, round(v + (255 - v) * amount)) for v in c)
|
||
|
||
|
||
def save(img, name):
|
||
path = os.path.join(OUT, name)
|
||
img.save(path)
|
||
print("wrote %s (%dx%d)" % (name, img.width, img.height))
|
||
|
||
|
||
def slider_grabber(fill):
|
||
"""12×12 方柄:fill 实心 + PANEL_BORDER 1px 描边"""
|
||
img = Image.new("RGBA", (12, 12), (0, 0, 0, 0))
|
||
for x in range(12):
|
||
for y in range(12):
|
||
edge = x in (0, 11) or y in (0, 11)
|
||
img.putpixel((x, y), PANEL_BORDER + (255,) if edge else fill + (255,))
|
||
return img
|
||
|
||
|
||
def spinbox_updown():
|
||
"""16×16 ▲▼ 合一:上三角 y2-7、下三角 y9-14,各宽 12(INK)"""
|
||
img = Image.new("RGBA", (16, 16), (0, 0, 0, 0))
|
||
# 上三角:顶行中心 2px(x7-8),第 6 行满宽 x2-13
|
||
for row in range(6):
|
||
half = 1 + row # 第 row 行左右各扩 1
|
||
for x in range(8 - half, 8 + half):
|
||
img.putpixel((x, 2 + row), INK + (255,))
|
||
# 下三角:镜像(第 row 行满宽在底)
|
||
for row in range(6):
|
||
half = 6 - row
|
||
for x in range(8 - half, 8 + half):
|
||
img.putpixel((x, 9 + row), INK + (255,))
|
||
return img
|
||
|
||
|
||
def option_arrow():
|
||
"""8×8 向下三角(OptionButton 箭头)"""
|
||
img = Image.new("RGBA", (8, 8), (0, 0, 0, 0))
|
||
for row in range(5):
|
||
half = row + 1
|
||
for x in range(4 - half, 4 + half - 1):
|
||
img.putpixel((x, 1 + row), INK + (255,))
|
||
return img
|
||
|
||
|
||
def checkbox(border, mark):
|
||
"""14×14 勾选框:PANEL_DATA 底 + 1px 边;mark 非 None 时画 2px 对勾"""
|
||
img = Image.new("RGBA", (14, 14), (0, 0, 0, 0))
|
||
for x in range(14):
|
||
for y in range(14):
|
||
edge = x in (0, 13) or y in (0, 13)
|
||
img.putpixel((x, y), border + (255,) if edge else PANEL_DATA + (255,))
|
||
if mark is not None:
|
||
# 对勾:(3,7)→(6,10)→(11,3),2px 粗(内区 1..12)
|
||
seg = [(3, 7), (4, 7), (4, 8), (5, 8), (5, 9), (6, 9),
|
||
(6, 10), (7, 10), (7, 11), (8, 11), (8, 10), (9, 10),
|
||
(9, 9), (10, 9), (10, 8), (11, 8),
|
||
(11, 7), (10, 7), (10, 6), (9, 6), (9, 5), (8, 5),
|
||
(8, 4), (7, 4), (7, 3), (6, 3)]
|
||
for (x, y) in seg:
|
||
if 1 <= x <= 12 and 1 <= y <= 12:
|
||
img.putpixel((x, y), mark + (255,))
|
||
return img
|
||
|
||
|
||
def main():
|
||
os.makedirs(OUT, exist_ok=True)
|
||
save(slider_grabber(CANDLE), "slider_grabber.png")
|
||
save(slider_grabber(lighten(CANDLE)), "slider_grabber_highlight.png")
|
||
save(slider_grabber(STONE_DEEP), "slider_grabber_disabled.png")
|
||
save(spinbox_updown(), "spinbox_updown.png")
|
||
save(option_arrow(), "option_arrow.png")
|
||
save(checkbox(PANEL_BORDER, None), "checkbox_unchecked.png")
|
||
save(checkbox(PANEL_BORDER, INK), "checkbox_checked.png")
|
||
save(checkbox(STONE_DEEP, None), "checkbox_unchecked_disabled.png")
|
||
save(checkbox(STONE_DEEP, STONE_DEEP), "checkbox_checked_disabled.png")
|
||
print("done → %s" % OUT)
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|