feat: refresh creation config and visual assets
This commit is contained in:
112
scripts/generate-taonier-short-foot-creature-contact-sheet.py
Normal file
112
scripts/generate-taonier-short-foot-creature-contact-sheet.py
Normal file
@@ -0,0 +1,112 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from PIL import Image, ImageDraw, ImageFont, ImageOps
|
||||
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parents[1]
|
||||
OUTPUT_DIR = (
|
||||
REPO_ROOT
|
||||
/ "public"
|
||||
/ "branding"
|
||||
/ "taonier-logo-short-foot-creature-concepts"
|
||||
)
|
||||
CONTACT_SHEET_PATH = OUTPUT_DIR / "taonier-logo-short-foot-creature-contact-sheet.png"
|
||||
|
||||
ITEMS = [
|
||||
("01 弯角泥团", "taonier-short-foot-creature-01-curled-tip"),
|
||||
("02 软芽泥团", "taonier-short-foot-creature-02-soft-sprout"),
|
||||
("03 波浪小怪", "taonier-short-foot-creature-03-wave-tuft"),
|
||||
("04 圆角小怪", "taonier-short-foot-creature-04-round-horn"),
|
||||
("05 低趴泥团", "taonier-short-foot-creature-05-low-squat"),
|
||||
("06 偏心灵体", "taonier-short-foot-creature-06-asymmetric-charm"),
|
||||
("07 头像强识别", "taonier-short-foot-creature-07-avatar-bold"),
|
||||
("08 商标轮廓", "taonier-short-foot-creature-08-vector-outline"),
|
||||
]
|
||||
|
||||
|
||||
def load_font(size: int) -> ImageFont.FreeTypeFont | ImageFont.ImageFont:
|
||||
candidates = [
|
||||
Path("C:/Windows/Fonts/msyh.ttc"),
|
||||
Path("C:/Windows/Fonts/simhei.ttf"),
|
||||
Path("C:/Windows/Fonts/simsun.ttc"),
|
||||
]
|
||||
for candidate in candidates:
|
||||
if candidate.exists():
|
||||
return ImageFont.truetype(str(candidate), size)
|
||||
return ImageFont.load_default()
|
||||
|
||||
|
||||
def find_image(stem: str) -> Path | None:
|
||||
for extension in ("png", "webp", "jpg", "jpeg"):
|
||||
candidate = OUTPUT_DIR / f"{stem}.{extension}"
|
||||
if candidate.exists():
|
||||
return candidate
|
||||
return None
|
||||
|
||||
|
||||
def bw_preview(image: Image.Image, size: int) -> Image.Image:
|
||||
thumb = ImageOps.grayscale(image).resize((size, size), Image.Resampling.LANCZOS)
|
||||
return ImageOps.autocontrast(thumb).convert("RGB")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
cell_size = 300
|
||||
label_height = 58
|
||||
test_height = 46
|
||||
gap = 24
|
||||
columns = 4
|
||||
rows = 2
|
||||
cell_total_height = cell_size + label_height + test_height
|
||||
width = columns * cell_size + (columns + 1) * gap
|
||||
height = rows * cell_total_height + (rows + 1) * gap
|
||||
|
||||
sheet = Image.new("RGB", (width, height), "#f0ebe5")
|
||||
draw = ImageDraw.Draw(sheet)
|
||||
label_font = load_font(20)
|
||||
test_font = load_font(14)
|
||||
|
||||
for index, (label, stem) in enumerate(ITEMS):
|
||||
row = index // columns
|
||||
column = index % columns
|
||||
x = gap + column * (cell_size + gap)
|
||||
y = gap + row * (cell_total_height + gap)
|
||||
|
||||
image_path = find_image(stem)
|
||||
if image_path is None:
|
||||
continue
|
||||
|
||||
source = Image.open(image_path).convert("RGB")
|
||||
thumbnail = source.resize((cell_size, cell_size), Image.Resampling.LANCZOS)
|
||||
sheet.paste(thumbnail, (x, y))
|
||||
|
||||
draw.rounded_rectangle(
|
||||
(x, y + cell_size, x + cell_size, y + cell_size + label_height),
|
||||
radius=8,
|
||||
fill="#fffdf8",
|
||||
)
|
||||
text_box = draw.textbbox((0, 0), label, font=label_font)
|
||||
text_x = x + (cell_size - (text_box[2] - text_box[0])) / 2
|
||||
text_y = y + cell_size + (label_height - (text_box[3] - text_box[1])) / 2 - 2
|
||||
draw.text((text_x, text_y), label, fill="#302a25", font=label_font)
|
||||
|
||||
test_y = y + cell_size + label_height
|
||||
draw.rounded_rectangle(
|
||||
(x, test_y, x + cell_size, test_y + test_height),
|
||||
radius=8,
|
||||
fill="#f7f3ed",
|
||||
)
|
||||
tiny = source.resize((32, 32), Image.Resampling.LANCZOS)
|
||||
mono = bw_preview(source, 32)
|
||||
sheet.paste(tiny, (x + 68, test_y + 7))
|
||||
sheet.paste(mono, (x + 122, test_y + 7))
|
||||
draw.text((x + 166, test_y + 14), "32px / BW", fill="#5c5148", font=test_font)
|
||||
|
||||
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
|
||||
sheet.save(CONTACT_SHEET_PATH, quality=95)
|
||||
print(CONTACT_SHEET_PATH)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user