Files
gplan-2/tools/gen_theme_icons.py
T

121 lines
5.1 KiB
Python
Raw 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.
#!/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()