Created
June 24, 2023 02:36
-
-
Save MineRobber9000/0e8081068320454321adbeca918b9942 to your computer and use it in GitHub Desktop.
TIC-80 graphics converter. Requires pillow.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"""TIC-80 graphics converter. Requires pillow. | |
usage: tic80gfx.py [-b BACKGROUND_COLOR] [-c COMMENT] [-p PALETTE] image index output | |
positional arguments: | |
image The image to convert. | |
index The index number for the top-left sprite. | |
output Where the converted image data will be saved. | |
options: | |
-b BACKGROUND_COLOR, --background-color BACKGROUND_COLOR | |
A background color, as a palette index. | |
-c COMMENT, --comment COMMENT | |
The prefix for comments. Defaults to -- (Lua comments) | |
-p PALETTE, --palette PALETTE | |
A palette to use, in GPL (GIMP Palette) format. | |
""" | |
from PIL import Image, ImagePalette | |
import argparse, atexit | |
SWEETIE_16 = ImagePalette.ImagePalette(palette=b"\x1a\x1c,]']\xb1>S\xef}W\xff\xcdu\xa7\xf0p8\xb7d%qy)6o;]\xc9A\xa6\xf6s\xef\xf7\xf4\xf4\xf4\x94\xb0\xc2Vl\x863<W") | |
def read_gpl_file(fn): | |
palette = bytearray() | |
with open(fn,"r") as fp: | |
if fp.readline().strip()!="GIMP Palette": | |
raise TypeError(f"Invalid GIMP palette {fn}!") | |
while (line:=fp.readline()): | |
parts = line.strip().split() | |
if parts[0].endswith(":"): continue | |
if len(parts)<3: | |
raise ValueError("Invalid color entry {line!r}!") | |
color = [int(x) for x in parts[:3]] | |
palette.extend(color) | |
fp.close() | |
return ImagePalette.ImagePalette(palette=palette) | |
def getcolor(p,i): | |
for color,index in p.colors.items(): | |
if index==i: return color | |
raise ValueError("No such palette index "+i) | |
TO_CLOSE=[] | |
@atexit.register | |
def closeall(): | |
for item in TO_CLOSE: | |
item.close() | |
def FileType(*args,**kwargs): | |
t=argparse.FileType(*args,**kwargs) | |
def ret(*args2,**kwargs2): | |
rv = t(*args2,**kwargs2) | |
TO_CLOSE.append(rv) | |
return rv | |
return ret | |
parser = argparse.ArgumentParser(description="TIC-80 graphics converter. Requires pillow.") | |
parser.add_argument("image",help="The image to convert.",type=FileType("rb")) | |
parser.add_argument("index",help="The index number for the top-left sprite.",type=int) | |
parser.add_argument("output",help="Where the converted image data will be saved.",type=FileType("w")) | |
parser.add_argument("-b","--background-color",help="A background color, as a palette index.",type=int,default=0) | |
parser.add_argument("-c","--comment",help="The prefix for comments. Defaults to -- (Lua comments)",default="--") | |
parser.add_argument("-p","--palette",help="A palette to use, in GPL (GIMP Palette) format.",type=read_gpl_file,default=SWEETIE_16) | |
args = parser.parse_args() | |
im = Image.open(args.image).convert("RGBA") | |
args.image.close() | |
if im.width>128 or im.height>128: | |
parser.error("Image too wide/tall for an image bank (max size 128x128)") | |
im2 = Image.new("RGBA",im.size,getcolor(args.palette,args.background_color)) | |
im = Image.alpha_composite(im2,im).convert("RGB") | |
p = Image.new("P",(1,1)) # for some reason Image.quantize can't just take a plain ImagePalette :( | |
p.putpalette(args.palette) | |
im = im.quantize(palette=p) # quantize to TIC-80 palette | |
width, height = im.size | |
sprites = {} | |
for y in range(0,height,8): | |
for x in range(0,width,8): | |
data = [] | |
for col in range(y,min(y+8,height)): | |
r=[] | |
for row in range(x,min(x+8,width)): | |
r.append(im.getpixel((row,col))) | |
while len(r)<8: r.append(args.background_color) | |
data.extend(r) | |
while len(data)<64: data.append(args.background_color) | |
sprites[y//8,x//8]=data | |
y = min(sprites.keys())[0] | |
output = [] | |
for y,x in sorted(sprites.keys()): | |
index = args.index+(y*16)+x | |
output.append(f"{args.comment} {index:03d}:"+''.join(map(lambda n: f"{n:x}",sprites[y,x]))) | |
args.output.write("\n".join(output)) | |
args.output.close() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment