for name, info in frames.items(): frame = info['frame'] x, y, w, h = frame['x'], frame['y'], frame['w'], frame['h'] # Extract sub-image sprite = atlas.crop((x, y, x+w, y+h)) # Handle rotation (example for 90° clockwise) if info.get('rotated', False): sprite = sprite.rotate(-90, expand=True) # Handle trimming: embed into sourceSize canvas if info.get('trimmed', False): src_w = info['sourceSize']['w'] src_h = info['sourceSize']['h'] offset_x = info['spriteSourceSize']['x'] offset_y = info['spriteSourceSize']['y'] new_img = Image.new('RGBA', (src_w, src_h), (0,0,0,0)) new_img.paste(sprite, (offset_x, offset_y)) sprite = new_img # Save safe_name = Path(name).stem sprite.save(output_path / f"safe_name.png")
"frames": "player_idle_01.png": "frame": "x": 2, "y": 10, "w": 64, "h": 64, "rotated": false, "trimmed": false, "spriteSourceSize": "x": 0, "y": 0, "w": 64, "h": 64, "sourceSize": "w": 64, "h": 64 texture atlas extractor
frames = data.get('frames', data) # handle different JSON structures for name, info in frames
This naive method works for atlases with transparent gaps between sprites. h = frame['x']
import json from PIL import Image from pathlib import Path def extract_atlas(atlas_path: str, metadata_path: str, output_dir: str): atlas = Image.open(atlas_path) with open(metadata_path, 'r') as f: data = json.load(f)
output_path = Path(output_dir) output_path.mkdir(exist_ok=True)
For most practical needs, using an existing tool with metadata support is recommended. When metadata is absent, a connected‑component based blind extractor provides a good starting point.