""" 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)