main
png2swc.py
1#!/usr/bin/env python3
2
3import getopt
4import subprocess
5import sys
6from pathlib import Path
7# yes its python, sorry
8
9PARTS = [
10 ("top_left", "top_left.png"),
11 ("top", "top.png"),
12 ("top_right", "top_right.png"),
13 ("left", "left.png"),
14 ("right", "right.png"),
15 ("bottom_left", "bottom_left.png"),
16 ("bottom", "bottom.png"),
17 ("bottom_right", "bottom_right.png"),
18]
19
20
21def usage(file):
22 file.write(
23 "usage: png2swc.py [-a asset-dir] [-o output] [-s symbol]\n"
24 )
25
26
27def run(cmd):
28 return subprocess.run(cmd, check=True, capture_output=True)
29
30
31def image_size(path: Path):
32 result = run(["magick", str(path), "-format", "%w %h", "info:"])
33 width, height = result.stdout.decode("utf-8").strip().split()
34 return int(width), int(height)
35
36
37def image_argb8888(path: Path):
38 width, height = image_size(path)
39 result = run(["magick", str(path), "rgba:-"])
40 raw = result.stdout
41 expected = width * height * 4
42 if len(raw) != expected:
43 raise ValueError(f"unexpected byte count for {path}: {len(raw)} != {expected}")
44
45 pixels = []
46 for i in range(0, len(raw), 4):
47 r, g, b, a = raw[i:i + 4]
48 pixels.append((a << 24) | (r << 16) | (g << 8) | b)
49 return width, height, pixels
50
51
52def format_pixels(name, pixels):
53 lines = []
54 row = []
55 for i, pixel in enumerate(pixels, start=1):
56 row.append(f"0x{pixel:08x}u")
57 if i % 8 == 0:
58 lines.append(", ".join(row))
59 row = []
60 if row:
61 lines.append(", ".join(row))
62
63 joined = ",\n ".join(lines)
64 return f"static const uint32_t {name}_pixels[] = {{\n {joined}\n}};\n"
65
66
67def generate_header(asset_dir: Path, output: Path, symbol: str):
68 arrays = []
69 parts = []
70
71 for part_name, filename in PARTS:
72 path = asset_dir / filename
73 width, height, pixels = image_argb8888(path)
74 arrays.append(format_pixels(f"{symbol}_{part_name}", pixels))
75 parts.append(
76 "static const struct swc_decor_part "
77 f"{symbol}_{part_name} = {{ .width = {width}u, .height = {height}u, "
78 f".stride = {width * 4}u, .data = {symbol}_{part_name}_pixels }};\n"
79 )
80
81 body = "\n".join(arrays + parts)
82 mapping = (
83 f"static const struct swc_decor_parts {symbol}_parts = {{\n"
84 f" .top_left = {symbol}_top_left,\n"
85 f" .top = {symbol}_top,\n"
86 f" .top_right = {symbol}_top_right,\n"
87 f" .left = {symbol}_left,\n"
88 f" .right = {symbol}_right,\n"
89 f" .bottom_left = {symbol}_bottom_left,\n"
90 f" .bottom = {symbol}_bottom,\n"
91 f" .bottom_right = {symbol}_bottom_right,\n"
92 f"}};\n"
93 )
94
95 guard = f"{symbol.upper()}_H"
96 text = (
97 f"#ifndef {guard}\n"
98 f"#define {guard}\n\n"
99 f"#include <stdint.h>\n"
100 f"#include <swc.h>\n\n"
101 f"/* generated from {asset_dir.name} png decor parts, probably dont edit this */\n\n"
102 f"{body}\n"
103 f"{mapping}\n"
104 f"#endif\n"
105 )
106 output.write_text(text, encoding="ascii")
107
108
109def main():
110 asset_dir = "."
111 output = "parts.h"
112 symbol = "afterstep_qnx"
113
114 try:
115 opts, args = getopt.getopt(
116 sys.argv[1:], "a:o:s:h", ["asset-dir=", "output=", "symbol=", "help"]
117 )
118 except getopt.GetoptError:
119 usage(sys.stderr)
120 raise SystemExit(1)
121
122 for opt, val in opts:
123 if opt in ("-a", "--asset-dir"):
124 asset_dir = val
125 elif opt in ("-o", "--output"):
126 output = val
127 elif opt in ("-s", "--symbol"):
128 symbol = val
129 elif opt in ("-h", "--help"):
130 usage(sys.stdout)
131 raise SystemExit(0)
132
133 if args:
134 usage(sys.stderr)
135 raise SystemExit(1)
136
137 generate_header(Path(asset_dir).resolve(), Path(output).resolve(), symbol)
138
139
140if __name__ == "__main__":
141 main()