"""
Simple Sprite Splitter
上傳圖片 → 輸入 cols/rows → 下載分割後的 ZIP
"""
import io
import zipfile
from fastapi import FastAPI, File, Form, UploadFile
from fastapi.responses import HTMLResponse, PlainTextResponse, StreamingResponse
from PIL import Image
app = FastAPI()
HTML = """
Sprite Splitter
📦 Sprite Splitter
快速分割圖片為網格並打包為 ZIP
預覽(分割線)
尚未上傳圖片
"""
@app.get("/", response_class=HTMLResponse)
async def index():
return HTML
@app.post("/process")
async def process(
file: UploadFile = File(...),
cols: int = Form(...),
rows: int = Form(...),
):
"""分割圖片並打包為 ZIP。"""
raw = await file.read()
try:
src = Image.open(io.BytesIO(raw))
src = src.convert("RGBA")
except Exception as e:
return PlainTextResponse(f"圖片載入失敗:{str(e)}", status_code=400)
W, H = src.size
tile_w = W // cols
tile_h = H // rows
tiles: list[Image.Image] = []
for r in range(rows):
for c in range(cols):
x0, y0 = c * tile_w, r * tile_h
cropped = src.crop((x0, y0, x0 + tile_w, y0 + tile_h))
tiles.append(cropped)
buf = io.BytesIO()
with zipfile.ZipFile(buf, "w", zipfile.ZIP_DEFLATED) as zf:
for idx, tile in enumerate(tiles):
r, c = divmod(idx, cols)
tb = io.BytesIO()
tile.save(tb, format="PNG")
zf.writestr(f"tile_r{r:02d}_c{c:02d}.png", tb.getvalue())
buf.seek(0)
return StreamingResponse(
buf,
media_type="application/zip",
headers={"Content-Disposition": "attachment; filename=sprite_split.zip"},
)
if __name__ == "__main__":
import uvicorn
uvicorn.run("sprite_splitter:app", host="0.0.0.0", port=8004, reload=True)