background: move background-packing to new file

This commit is contained in:
Zack Buhman 2025-09-02 19:26:55 -05:00
parent f4fe943b9b
commit 0f4f1b69a9
3 changed files with 219 additions and 206 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*.pyc
__pycache__

View File

@ -1,11 +1,8 @@
import struct import struct
import sys import sys
from dataclasses import dataclass from dataclasses import dataclass
from pprint import pprint, pformat
import textwrap
from typing import Union, Optional from typing import Union, Optional
import zlib import zlib
from operator import itemgetter
class CustomInt: class CustomInt:
value: int value: int
@ -486,212 +483,58 @@ def parse_cel_chunk(mem):
return cel_chunk, mem return cel_chunk, mem
with open(sys.argv[1], 'rb') as f:
buf = f.read()
mem = memoryview(buf)
def pprinti(o, i): def parse_file(mem):
s = pformat(o) header, mem = parse_header(mem)
print(textwrap.indent(s, ' ' * i)) #pprint(header)
assert header.color_depth == 8, header.color_depth
def pack_bgr555(red, green, blue): frame_header, mem = parse_frame_header(mem)
bgr = ( #pprint(frame_header)
((red >> 3) << 0) |
((green >> 3) << 5) |
((blue >> 3) << 10)
)
return struct.pack(">H", bgr)
def pack_index(i): tilesets = dict() # by tileset id
return struct.pack(">I", i) layers = []
palette = None
cel_chunks = dict() # by layer index
def pack_old_palette_chunk(old_palette_chunk): for _ in range(frame_header.number_of_chunks):
with open("palette.bin", "wb") as f: chunk, mem = parse_chunk(mem)
for color in old_palette_chunk.packets[0].colors: #pprinti(chunk, 1)
f.write(pack_bgr555(*color)) if chunk.chunk_type == 0x4:
old_palette_chunk, _ = parse_old_palette_chunk(chunk.data)
#pprinti(old_palette_chunk, 2)
assert palette is None
palette = old_palette_chunk
elif chunk.chunk_type == 0x2019:
palette_chunk, _ = parse_palette_chunk(chunk.data)
#pprinti(palette_chunk, 2)
assert palette is None
palette = palette_chunk
elif chunk.chunk_type == 0x2023:
tileset_chunk, _ = parse_tileset_chunk(chunk.data)
assert tileset_chunk.tileset_id not in tilesets
tilesets[tileset_chunk.tileset_id] = tileset_chunk
#pprinti(tileset_chunk, 2)
elif chunk.chunk_type == 0x2004:
layer_chunk, _ = parse_layer_chunk(chunk.data, header.flags)
assert layer_chunk.layer_type == 2
layers.append(layer_chunk)
#pprinti(layer_chunk, 2)
elif chunk.chunk_type == 0x2005:
cel_chunk, _ = parse_cel_chunk(chunk.data)
#pprinti(cel_chunk, 2)
assert cel_chunk.layer_index not in cel_chunks
cel_chunks[cel_chunk.layer_index] = cel_chunk
elif chunk.chunk_type == 0x2020:
# user data
pass
elif chunk.chunk_type == 0x2007:
# color profile
pass
else:
print("unhandled chunk: ")
pprinti(chunk, 1)
def pack_palette_chunk(palette_chunk): assert palette is not None
with open("palette.bin", "wb") as f:
assert palette_chunk.first_color_index_to_change == 0
for entry in palette_chunk.entries: return tilesets, layers, palette, cel_chunks
color = (entry.red, entry.green, entry.blue)
f.write(pack_bgr555(*color))
print("palette.bin", f.tell(), file=sys.stderr)
def pack_palette(palette):
if type(palette) is PaletteChunk:
pack_palette_chunk(palette)
elif type(palette) is OldPaletteChunk:
pack_old_palette_chunk(palette)
else:
assert False, type(palette)
def pack_character_2x2(tileset_chunk, offset):
#tileset_chunk.number_of_tiles,
#tileset_chunk.tile_width,
#tileset_chunk.tile_height,
assert tileset_chunk.tile_width == 16
assert tileset_chunk.tile_height == 16
assert type(tileset_chunk.data) == TilesetChunkInternal
buf = bytearray(16 * 16)
for cell_ix in range(4):
for y in range(8):
for x in range(8):
tileset_x = 8 * (cell_ix % 2) + x
tileset_y = 8 * (cell_ix // 2) + y
px = tileset_chunk.data.pixel[offset + tileset_y * 16 + tileset_x]
buf[cell_ix * 8 * 8 + y * 8 + x] = px
return bytes(buf)
def pack_character_1x1(tileset_chunk, offset):
assert tileset_chunk.tile_width == 8
assert tileset_chunk.tile_height == 8
assert type(tileset_chunk.data) == TilesetChunkInternal
buf = bytearray(8 * 8)
for y in range(8):
for x in range(8):
tileset_x = x
tileset_y = y
px = tileset_chunk.data.pixel[offset + tileset_y * 8 + tileset_x]
buf[y * 8 + x] = px
return bytes(buf)
def pack_character_patterns(filename, tileset_chunk):
with open(filename, "wb") as f:
for i in range(tileset_chunk.number_of_tiles):
offset = tileset_chunk.tile_width * tileset_chunk.tile_height * i
if tileset_chunk.tile_width == 8 and tileset_chunk.tile_height == 8:
buf = pack_character_1x1(tileset_chunk, offset)
elif tileset_chunk.tile_width == 16 and tileset_chunk.tile_height == 16:
buf = pack_character_2x2(tileset_chunk, offset)
else:
assert False, (tileset_chunk.tile_width, tileset_chunk.tile_height)
f.write(buf)
print(filename, f.tell(), file=sys.stderr)
def pack_pattern_name_table(filename, cel_chunk, x_cells, y_cells):
with open(filename, "wb") as f:
assert type(cel_chunk.data) == CelChunk_CompressedTilemap
#assert cel_chunk.data.width_in_number_of_tiles <= 64
#assert cel_chunk.data.height_in_number_of_tiles <= 64
tile_width = cel_chunk.data.width_in_number_of_tiles
tile_height = cel_chunk.data.height_in_number_of_tiles
print(tile_width, tile_height)
h_pages = ((tile_width + (x_cells - 1)) & (~(x_cells - 1))) // x_cells
v_pages = ((tile_height + (y_cells - 1)) & (~(y_cells - 1))) // y_cells
if h_pages > 2:
h_pages = 2
if v_pages > 2:
v_pages = 2
print("h_pages, v_pages", h_pages, v_pages)
for v_page in range(v_pages):
for h_page in range(h_pages):
for y in range(y_cells):
for x in range(x_cells):
tx = (h_page * x_cells) + x
ty = (v_page * y_cells) + y
if tx >= tile_width or ty >= tile_height:
f.write(pack_index(0))
else:
cel_chunk_ix = ty * tile_width + tx
tile_data = cel_chunk.data.tile[cel_chunk_ix]
tile_id = tile_data & cel_chunk.data.bitmask_for_tile_id.value
x_flip = (tile_data & cel_chunk.data.bitmask_for_x_flip.value) != 0
y_flip = (tile_data & cel_chunk.data.bitmask_for_y_flip.value) != 0
pattern = (int(y_flip) << 31) | (int(x_flip) << 30) | tile_id
f.write(pack_index(pattern))
print(filename, f.tell(), file=sys.stderr)
header, mem = parse_header(mem)
#pprint(header)
assert header.color_depth == 8, header.color_depth
frame_header, mem = parse_frame_header(mem)
#pprint(frame_header)
tilesets = dict() # by tileset id
layers = []
palette = None
cel_chunks = dict() # by layer index
for _ in range(frame_header.number_of_chunks):
chunk, mem = parse_chunk(mem)
#pprinti(chunk, 1)
if chunk.chunk_type == 0x4:
old_palette_chunk, _ = parse_old_palette_chunk(chunk.data)
#pprinti(old_palette_chunk, 2)
assert palette is None
palette = old_palette_chunk
elif chunk.chunk_type == 0x2019:
palette_chunk, _ = parse_palette_chunk(chunk.data)
#pprinti(palette_chunk, 2)
assert palette is None
palette = palette_chunk
elif chunk.chunk_type == 0x2023:
tileset_chunk, _ = parse_tileset_chunk(chunk.data)
assert tileset_chunk.tileset_id not in tilesets
tilesets[tileset_chunk.tileset_id] = tileset_chunk
#pprinti(tileset_chunk, 2)
elif chunk.chunk_type == 0x2004:
layer_chunk, _ = parse_layer_chunk(chunk.data, header.flags)
assert layer_chunk.layer_type == 2
layers.append(layer_chunk)
#pprinti(layer_chunk, 2)
elif chunk.chunk_type == 0x2005:
cel_chunk, _ = parse_cel_chunk(chunk.data)
#pprinti(cel_chunk, 2)
assert cel_chunk.layer_index not in cel_chunks
cel_chunks[cel_chunk.layer_index] = cel_chunk
elif chunk.chunk_type == 0x2020:
# user data
pass
elif chunk.chunk_type == 0x2007:
# color profile
pass
else:
print("unhandled chunk: ")
pprinti(chunk, 1)
assert palette is not None
pack_palette(palette)
for tileset_index, tileset_chunk in sorted(tilesets.items(), key=itemgetter(0)):
filename = f"character_pattern__tileset_{tileset_index}.bin"
pack_character_patterns(filename, tileset_chunk)
for layer_index, cel_chunk in sorted(cel_chunks.items(), key=itemgetter(0)):
filename = f"pattern_name_table__layer_{layer_index}.bin"
#layers[layer_index]
print(f"layer={layer_index} layer_name={layers[layer_index].layer_name} tileset={layers[layer_index].tileset_index}");
tileset_chunk = tilesets[layers[layer_index].tileset_index]
x_cells = 64 // (tileset_chunk.tile_width // 8)
y_cells = 64 // (tileset_chunk.tile_height // 8)
pack_pattern_name_table(filename, cel_chunk, x_cells, y_cells)
#for layer_index, layer_chunk in enumerate(layers):
# print(f"layer={layer_index} layer_name={layer_chunk.layer_name} tileset={layer_chunk.tileset_index}");

168
background.py Normal file
View File

@ -0,0 +1,168 @@
import sys
from pprint import pprint, pformat
import textwrap
import struct
from operator import itemgetter
from aseprite import parse_file
from aseprite import PaletteChunk, OldPaletteChunk, TilesetChunkInternal, CelChunk_CompressedTilemap
def pprinti(o, i):
s = pformat(o)
print(textwrap.indent(s, ' ' * i))
def pack_bgr555(red, green, blue):
bgr = (
((red >> 3) << 0) |
((green >> 3) << 5) |
((blue >> 3) << 10)
)
return struct.pack(">H", bgr)
def pack_index(i):
return struct.pack(">I", i)
def pack_old_palette_chunk(old_palette_chunk):
with open("palette.bin", "wb") as f:
for color in old_palette_chunk.packets[0].colors:
f.write(pack_bgr555(*color))
def pack_palette_chunk(palette_chunk):
with open("palette.bin", "wb") as f:
assert palette_chunk.first_color_index_to_change == 0
for entry in palette_chunk.entries:
color = (entry.red, entry.green, entry.blue)
f.write(pack_bgr555(*color))
print("palette.bin", f.tell(), file=sys.stderr)
def pack_palette(palette):
if type(palette) is PaletteChunk:
pack_palette_chunk(palette)
elif type(palette) is OldPaletteChunk:
pack_old_palette_chunk(palette)
else:
assert False, type(palette)
def pack_character_2x2(tileset_chunk, offset):
#tileset_chunk.number_of_tiles,
#tileset_chunk.tile_width,
#tileset_chunk.tile_height,
assert tileset_chunk.tile_width == 16
assert tileset_chunk.tile_height == 16
assert type(tileset_chunk.data) == TilesetChunkInternal
buf = bytearray(16 * 16)
for cell_ix in range(4):
for y in range(8):
for x in range(8):
tileset_x = 8 * (cell_ix % 2) + x
tileset_y = 8 * (cell_ix // 2) + y
px = tileset_chunk.data.pixel[offset + tileset_y * 16 + tileset_x]
buf[cell_ix * 8 * 8 + y * 8 + x] = px
return bytes(buf)
def pack_character_1x1(tileset_chunk, offset):
assert tileset_chunk.tile_width == 8
assert tileset_chunk.tile_height == 8
assert type(tileset_chunk.data) == TilesetChunkInternal
buf = bytearray(8 * 8)
for y in range(8):
for x in range(8):
tileset_x = x
tileset_y = y
px = tileset_chunk.data.pixel[offset + tileset_y * 8 + tileset_x]
buf[y * 8 + x] = px
return bytes(buf)
def pack_character_patterns(filename, tileset_chunk):
with open(filename, "wb") as f:
for i in range(tileset_chunk.number_of_tiles):
offset = tileset_chunk.tile_width * tileset_chunk.tile_height * i
if tileset_chunk.tile_width == 8 and tileset_chunk.tile_height == 8:
buf = pack_character_1x1(tileset_chunk, offset)
elif tileset_chunk.tile_width == 16 and tileset_chunk.tile_height == 16:
buf = pack_character_2x2(tileset_chunk, offset)
else:
assert False, (tileset_chunk.tile_width, tileset_chunk.tile_height)
f.write(buf)
print(filename, f.tell(), file=sys.stderr)
def pack_pattern_name_table(filename, cel_chunk, x_cells, y_cells):
with open(filename, "wb") as f:
assert type(cel_chunk.data) == CelChunk_CompressedTilemap
#assert cel_chunk.data.width_in_number_of_tiles <= 64
#assert cel_chunk.data.height_in_number_of_tiles <= 64
tile_width = cel_chunk.data.width_in_number_of_tiles
tile_height = cel_chunk.data.height_in_number_of_tiles
print(tile_width, tile_height)
h_pages = ((tile_width + (x_cells - 1)) & (~(x_cells - 1))) // x_cells
v_pages = ((tile_height + (y_cells - 1)) & (~(y_cells - 1))) // y_cells
if h_pages > 2:
h_pages = 2
if v_pages > 2:
v_pages = 2
print("h_pages, v_pages", h_pages, v_pages)
for v_page in range(v_pages):
for h_page in range(h_pages):
for y in range(y_cells):
for x in range(x_cells):
tx = (h_page * x_cells) + x
ty = (v_page * y_cells) + y
if tx >= tile_width or ty >= tile_height:
f.write(pack_index(0))
else:
cel_chunk_ix = ty * tile_width + tx
tile_data = cel_chunk.data.tile[cel_chunk_ix]
tile_id = tile_data & cel_chunk.data.bitmask_for_tile_id.value
x_flip = (tile_data & cel_chunk.data.bitmask_for_x_flip.value) != 0
y_flip = (tile_data & cel_chunk.data.bitmask_for_y_flip.value) != 0
pattern = (int(y_flip) << 31) | (int(x_flip) << 30) | tile_id
f.write(pack_index(pattern))
print(filename, f.tell(), file=sys.stderr)
with open(sys.argv[1], 'rb') as f:
buf = f.read()
mem = memoryview(buf)
tilesets, layers, palette, cel_chunks = parse_file(buf)
pack_palette(palette)
for tileset_index, tileset_chunk in sorted(tilesets.items(), key=itemgetter(0)):
filename = f"character_pattern__tileset_{tileset_index}.bin"
pack_character_patterns(filename, tileset_chunk)
for layer_index, cel_chunk in sorted(cel_chunks.items(), key=itemgetter(0)):
filename = f"pattern_name_table__layer_{layer_index}.bin"
#layers[layer_index]
print(f"layer={layer_index} layer_name={layers[layer_index].layer_name} tileset={layers[layer_index].tileset_index}");
tileset_chunk = tilesets[layers[layer_index].tileset_index]
x_cells = 64 // (tileset_chunk.tile_width // 8)
y_cells = 64 // (tileset_chunk.tile_height // 8)
pack_pattern_name_table(filename, cel_chunk, x_cells, y_cells)
#for layer_index, layer_chunk in enumerate(layers):
# print(f"layer={layer_index} layer_name={layer_chunk.layer_name} tileset={layer_chunk.tileset_index}");