Skip to content

Instantly share code, notes, and snippets.

@MineRobber9000
Created June 24, 2023 02:36
Show Gist options
  • Save MineRobber9000/0e8081068320454321adbeca918b9942 to your computer and use it in GitHub Desktop.
Save MineRobber9000/0e8081068320454321adbeca918b9942 to your computer and use it in GitHub Desktop.
TIC-80 graphics converter. Requires pillow.
"""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