Skip to content

Instantly share code, notes, and snippets.

@kennedy0
Last active April 11, 2024 04:53
Show Gist options
  • Save kennedy0/29eac8cd827edc9c1d0f056316f43e9c to your computer and use it in GitHub Desktop.
Save kennedy0/29eac8cd827edc9c1d0f056316f43e9c to your computer and use it in GitHub Desktop.
sprite_packer
import json
from pathlib import Path
from PIL import Image
# Build sprite list
sprites_folder = Path("./sprites")
sprites = []
for file in sprites_folder.iterdir():
if file.suffix == ".png":
sprite = Image.open(file)
sprites.append(sprite)
# Sort sprites from largest to smallest area
sprites.sort(key=lambda s: s.height * s.width, reverse=True)
# Create atlas
atlas_size = (512, 512)
while True:
atlas = Image.new(mode="RGBA", size=atlas_size)
sprite_data = {}
packing_failed = False
# Empty areas in the atlas
area = (0, 0, atlas_size[0], atlas_size[1]) # (x, y, w, h)
areas = [area]
# Pack sprites using the Guillotine algorithm
for sprite in sprites:
# Find an empty area
area = None
for a in areas:
x, y, w, h = a
if sprite.width <= w and sprite.height <= h:
# Add the sprite to the atlas
atlas.paste(sprite, box=(x, y))
sprite_name = Path(sprite.filename).name
sprite_data[sprite_name] = {'x': x, 'y': y, 'w': sprite.width, 'h': sprite.height}
area = a
break
# Area not found
if area is None:
packing_failed = True
atlas_size = (atlas_size[0] + 64, atlas_size[1] + 64)
print(f"Resizing atlas size to {atlas_size}")
break
# Split the area into new top and bottom areas
x, y, w, h = area
top = (x + sprite.width, y, w - sprite.width, sprite.height)
bottom = (x, y + sprite.height, w, h - sprite.height)
# Update the area list with the two new areas
areas.remove(area)
areas.append(top)
areas.append(bottom)
# Break the loop if we placed all the sprites
if not packing_failed:
print(f"Finished packing {len(sprites)} sprites")
break
# Save the atlas file
atlas_file = Path("./atlas.png")
atlas.save(atlas_file)
# Save the sprite data file
sprite_data_file = Path("./data.json")
with open(sprite_data_file, 'w') as fp:
json.dump(sprite_data, fp, indent=True)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment