summaryrefslogtreecommitdiff
path: root/gfx-iso/asset/mkasset.py
diff options
context:
space:
mode:
author3gg <3gg@shellblade.net>2023-07-26 08:39:37 -0700
committer3gg <3gg@shellblade.net>2023-07-26 08:39:37 -0700
commitcef3385c2bee0b098a7795548345a9281ace008e (patch)
treeb594e9cc151a4ae7fd8b5732cf349eb01f37683d /gfx-iso/asset/mkasset.py
parent48cef82988d6209987ae27fe29b72d7d5e402b3c (diff)
Add support for paletted sprites.
Diffstat (limited to 'gfx-iso/asset/mkasset.py')
-rw-r--r--gfx-iso/asset/mkasset.py127
1 files changed, 90 insertions, 37 deletions
diff --git a/gfx-iso/asset/mkasset.py b/gfx-iso/asset/mkasset.py
index b4e335f..3ca8a1d 100644
--- a/gfx-iso/asset/mkasset.py
+++ b/gfx-iso/asset/mkasset.py
@@ -165,44 +165,57 @@ def get_num_cols(image, sprite_width):
165 return 0 165 return 0
166 166
167 167
168def get_sprite_sheet_rows(input_filepath, sprite_width, sprite_height): 168def get_sprite_sheet_rows(im, sprite_width, sprite_height):
169 """Gets the individual rows of a sprite sheet. 169 """Gets the individual rows of a sprite sheet.
170 170
171 The input sprite sheet can have any number of rows. 171 The input sprite sheet can have any number of rows.
172 172
173 Returns a list of lists [[sprite bytes]], one inner list for the columns in 173 Returns a list of lists [[sprite]], one inner list for the columns in each
174 each row. 174 row.
175 """ 175 """
176 with Image.open(input_filepath) as im: 176 # Sprite sheet's width and height must be integer multiples of the
177 # Sprite sheet's width and height must be integer multiples of the 177 # sprite's width and height.
178 # sprite's width and height. 178 assert (im.width % sprite_width == 0)
179 assert (im.width % sprite_width == 0) 179 assert (im.height % sprite_height == 0)
180 assert (im.height % sprite_height == 0)
181 180
182 num_rows = im.height // sprite_height 181 num_rows = im.height // sprite_height
183 182
184 rows = [] 183 rows = []
185 for row in range(num_rows): 184 for row in range(num_rows):
186 # Get the number of columns. 185 # Get the number of columns.
187 upper = row * sprite_height 186 upper = row * sprite_height
188 lower = (row + 1) * sprite_height 187 lower = (row + 1) * sprite_height
189 whole_row = im.crop((0, upper, im.width, lower)) 188 whole_row = im.crop((0, upper, im.width, lower))
190 num_cols = get_num_cols(whole_row, sprite_width) 189 num_cols = get_num_cols(whole_row, sprite_width)
191 assert (num_cols > 0) 190 assert (num_cols > 0)
192 191
193 # Crop the row into N columns. 192 # Crop the row into N columns.
194 cols = [] 193 cols = []
195 for i in range(num_cols): 194 for i in range(num_cols):
196 left = i * sprite_width 195 left = i * sprite_width
197 right = (i + 1) * sprite_width 196 right = (i + 1) * sprite_width
198 sprite = im.crop((left, upper, right, lower)) 197 sprite = im.crop((left, upper, right, lower))
199 cols.append(sprite) 198 cols.append(sprite)
200 199
201 sprite_bytes = [sprite.convert('RGBA').tobytes() for sprite in cols] 200 assert (len(cols) == num_cols)
202 assert (len(sprite_bytes) == num_cols) 201 rows.append(cols)
203 rows.append(sprite_bytes) 202
204 203 return rows
205 return rows 204
205
206def make_image_from_rows(rows, sprite_width, sprite_height):
207 """Concatenate the rows into a single RGBA image."""
208 im_width = sprite_width * max(len(row) for row in rows)
209 im_height = len(rows) * sprite_height
210 im = Image.new('RGBA', (im_width, im_height))
211 y = 0
212 for row in rows:
213 x = 0
214 for sprite in row:
215 im.paste(sprite.convert('RGBA'), (x, y))
216 x += sprite_width
217 y += sprite_height
218 return im
206 219
207 220
208def convert_sprite_sheet(input_file_paths, sprite_width, sprite_height, 221def convert_sprite_sheet(input_file_paths, sprite_width, sprite_height,
@@ -217,25 +230,65 @@ def convert_sprite_sheet(input_file_paths, sprite_width, sprite_height,
217 sprite sheets. 230 sprite sheets.
218 """ 231 """
219 rows = [] 232 rows = []
233 for input_filepath in input_file_paths:
234 with Image.open(input_filepath) as sprite_sheet:
235 rows.extend(
236 get_sprite_sheet_rows(sprite_sheet, sprite_width,
237 sprite_height))
220 238
221 for sprite_sheet in input_file_paths: 239 im = make_image_from_rows(rows, sprite_width, sprite_height)
222 rows.extend( 240 im = im.convert(mode="P", palette=Image.ADAPTIVE, colors=256)
223 get_sprite_sheet_rows(sprite_sheet, sprite_width, sprite_height)) 241
242 # The sprite data in 'rows' is no longer needed.
243 # Keep just the number of columns per row.
244 rows = [len(row) for row in rows]
224 245
225 with open(output_filepath, 'bw') as output: 246 with open(output_filepath, 'bw') as output:
226 output.write(ctypes.c_uint16(sprite_width)) 247 output.write(ctypes.c_uint16(sprite_width))
227 output.write(ctypes.c_uint16(sprite_height)) 248 output.write(ctypes.c_uint16(sprite_height))
228 output.write(ctypes.c_uint16(len(rows))) 249 output.write(ctypes.c_uint16(len(rows)))
229 250
251 # Write palette.
252 # getpalette() returns 256 colors, but the palette might use less than
253 # that. getcolors() returns the number of unique colors.
254 # getpalette() also returns a flattened list, which is why we must *4.
255 num_colours = len(im.getcolors())
256 colours = im.getpalette(rawmode="RGBA")[:4 * num_colours]
257 palette = []
258 for i in range(0, 4 * num_colours, 4):
259 palette.append((colours[i], colours[i + 1], colours[i + 2],
260 colours[i + 3]))
261
262 output.write(ctypes.c_uint16(len(palette)))
263 output.write(bytearray(colours))
264
230 print(f"Sprite width: {sprite_width}") 265 print(f"Sprite width: {sprite_width}")
231 print(f"Sprite height: {sprite_height}") 266 print(f"Sprite height: {sprite_height}")
232 print(f"Rows: {len(rows)}") 267 print(f"Rows: {len(rows)}")
268 print(f"Colours: {len(palette)}")
269
270 # print("Palette")
271 # for i, colour in enumerate(palette):
272 # print(f"{i}: {colour}")
233 273
234 for sprites in rows: 274 for row, num_columns in enumerate(rows):
235 output.write(ctypes.c_uint16(len(sprites))) 275 output.write(ctypes.c_uint16(num_columns))
236 for sprite_bytes in sprites: 276 upper = row * sprite_height
277 lower = (row + 1) * sprite_height
278 for col in range(num_columns):
279 left = col * sprite_width
280 right = (col + 1) * sprite_width
281 sprite = im.crop((left, upper, right, lower))
282 sprite_bytes = sprite.tobytes()
283
284 assert (len(sprite_bytes) == sprite_width * sprite_height)
237 output.write(sprite_bytes) 285 output.write(sprite_bytes)
238 286
287 # if (row == 0) and (col == 0):
288 # print(f"Sprite: ({len(sprite_bytes)})")
289 # print(list(sprite_bytes))
290 # sprite.save("out.png")
291
239 292
240def main(): 293def main():
241 parser = argparse.ArgumentParser() 294 parser = argparse.ArgumentParser()