convert to and from RGBA images instead of palette based ones

This commit is contained in:
josch 2014-03-22 15:45:25 +01:00
parent 6c2557dec8
commit 7c5377d29b
5 changed files with 110 additions and 53 deletions

View file

@ -1,27 +0,0 @@
import crcmod
import colorsys
from PIL import ImageFont
font = ImageFont.truetype("/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf", 34)
def sanitize_filename(fname):
# find the first character outside range [32-126]
for i,c in enumerate(fname):
if ord(c) < 32 or ord(c) > 126:
break
return fname[:i]
def get_complement(r,g,b):
r = r/255.0
g = g/255.0
b = b/255.0
h,l,s = colorsys.rgb_to_hls(r, g, b)
if h > 0.5:
h -= 0.5
else:
h += 0.5
r,g,b = colorsys.hls_to_rgb(h, l, s)
return int(r*255), int(g*255), int(b*255)
crc24_func = crcmod.mkCrcFun(0x1864CFBL) # polynomial from libgcrypt

View file

@ -25,13 +25,7 @@ from PIL import Image, ImageDraw
from collections import defaultdict from collections import defaultdict
import os import os
import json import json
from common import crc24_func, font, sanitize_filename import numpy as np
def get_color(fname):
crc = crc24_func(fname)
# values 0-7 must not be used as they might represent transparency
# so we are left with 248 values
return 8+crc%248
def extract_def(infile,outdir): def extract_def(infile,outdir):
f = open(infile) f = open(infile)
@ -176,14 +170,33 @@ def extract_def(infile,outdir):
else: else:
print "unknown format: %d"%fmt print "unknown format: %d"%fmt
return False return False
im = Image.fromstring('P', (w,h),pixeldata) imp = Image.fromstring('P', (w,h),pixeldata)
imp.putpalette(palette)
# convert to RGBA
imrgb = imp.convert("RGBA")
# replace special colors
# 0 -> (0,0,0,0) = full transparency
# 1 -> (0,0,0,0x40) = shadow border
# 2 -> ???
# 3 -> ???
# 4 -> (0,0,0,0x80) = shadow body
# 5 -> (0,0,0,0) = selection highlight, treat as full transparency
# 6 -> (0,0,0,0x80) = shadow body below selection, treat as shadow body
# 7 -> (0,0,0,0x40) = shadow border below selection, treat as shadow border
pixrgb = np.array(imrgb)
pixp = np.array(imp)
pixrgb[pixp == 0] = (0,0,0,0)
pixrgb[pixp == 1] = (0,0,0,0x40)
pixrgb[pixp == 4] = (0,0,0,0x80)
pixrgb[pixp == 5] = (0,0,0,0)
pixrgb[pixp == 6] = (0,0,0,0x80)
pixrgb[pixp == 7] = (0,0,0,0x40)
imrgb = Image.fromarray(pixrgb)
im = Image.new('RGBA', (fw,fh), (0,0,0,0))
im.paste(imrgb,(lm,tm))
else: # either width or height is zero else: # either width or height is zero
im = None im = Image.new('RGBA', (fw,fh), (0,0,0,0))
imo = Image.new('P', (fw,fh)) im.save(outname)
imo.putpalette(palette)
if im:
imo.paste(im,(lm,tm))
imo.save(outname)
out_json["sequences"].append({"group":bid,"frames":frames}) out_json["sequences"].append({"group":bid,"frames":frames})
with open(os.path.join(outdir,"%s.json"%bn),"w+") as o: with open(os.path.join(outdir,"%s.json"%bn),"w+") as o:
json.dump(out_json,o,indent=4) json.dump(out_json,o,indent=4)

View file

@ -19,7 +19,12 @@
import struct import struct
from collections import defaultdict from collections import defaultdict
from common import sanitize_filename def sanitize_filename(fname):
# find the first character outside range [32-126]
for i,c in enumerate(fname):
if ord(c) < 32 or ord(c) > 126:
break
return fname[:i]
def main(infile): def main(infile):
f = open(infile) f = open(infile)
@ -28,8 +33,8 @@ def main(infile):
palette = [] palette = []
for i in range(256): for i in range(256):
r,g,b = struct.unpack("<BBB", f.read(3)) r,g,b = struct.unpack("<BBB", f.read(3))
palette.extend((r,g,b)) palette.append((r,g,b))
print palette print "palette: %s"%(' '.join(["#%02x%02x%02x"%(r,g,b) for r,g,b in palette]))
offsets = defaultdict(list) offsets = defaultdict(list)
for i in range(blocks): for i in range(blocks):
bid,entries,x_,y_ = struct.unpack("<IIII", f.read(16)) bid,entries,x_,y_ = struct.unpack("<IIII", f.read(16))

View file

@ -21,8 +21,6 @@ import struct
import os import os
from PIL import Image, ImageDraw from PIL import Image, ImageDraw
from common import crc24_func, get_complement, font
def is_pcx(data): def is_pcx(data):
size,width,height = struct.unpack("<III",data[:12]) size,width,height = struct.unpack("<III",data[:12])
return size == width*height or size == width*height*3 return size == width*height or size == width*height*3

View file

@ -21,6 +21,8 @@ import struct
import json import json
from collections import defaultdict from collections import defaultdict
from PIL import Image from PIL import Image
import numpy as np
ushrtmax = (1<<16)-1 ushrtmax = (1<<16)-1
def encode0(im): def encode0(im):
@ -172,6 +174,9 @@ def makedef(infile, outdir):
p = os.path.splitext(p)[0].lower() p = os.path.splitext(p)[0].lower()
d = os.path.dirname(infile) d = os.path.dirname(infile)
outname = os.path.join(outdir,p)+".def"
print "writing to %s"%outname
# sanity checks and fill infiles dict # sanity checks and fill infiles dict
for seq in in_json["sequences"]: for seq in in_json["sequences"]:
bid = seq["group"] bid = seq["group"]
@ -192,10 +197,13 @@ def makedef(infile, outdir):
w = (((w-1)>>5)+1)<<5 w = (((w-1)>>5)+1)<<5
rm = lm+w rm = lm+w
im = im.crop((lm,tm,rm,bm)) im = im.crop((lm,tm,rm,bm))
if im.mode != 'P': if im.mode == 'P':
print "input images must have a palette" cursig =(fw,fh,im.getpalette())
elif im.mode == 'RGBA':
cursig =(fw,fh,None)
else:
print "input images must be rgba or palette based"
return False return False
cursig =(fw,fh,im.getpalette())
if not sig: if not sig:
sig = cursig sig = cursig
else: else:
@ -204,16 +212,76 @@ def makedef(infile, outdir):
print sig print sig
print cursig print cursig
return False return False
data,size = fmtencoders[fmt](im) infiles[bid].append((lm,tm,im))
infiles[bid].append((w,h,lm,tm,data,size))
if len(infiles) == 0: if len(infiles) == 0:
print "no input files detected" print "no input files detected"
return False return False
fw,fh,pal = cursig fw,fh,pal = cursig
outname = os.path.join(outdir,p)+".def" numframes = sum(len(l) for l in infiles.values())
print "writing to %s"%outname
# input images were RGB, find a good common palette
if not pal:
# create a concatenation of all images to create a good common palette
concatim = Image.new("RGB",(fw,fh*numframes))
num = 0
for _,l in infiles.items():
for _,_,im in l:
concatim.paste(im, (0,fh*num))
num+=1
# convert that concatenation to a palette image to obtain a good common palette
concatim = concatim.convert("P", dither=None, colors=248, palette=Image.ADAPTIVE)
# concatenate the 248 colors to the 8 special ones
pal = [0x00, 0xff, 0xff, # full transparency
0xff, 0x96, 0xff, # shadow border
0xff, 0x64, 0xff, # ???
0xff, 0x32, 0xff, # ???
0xff, 0x00, 0xff, # shadow body
0xff, 0xff, 0x00, # selection highlight
0xb4, 0x00, 0xff, # shadow body below selection
0x00, 0xff, 0x00, # shadow border below selection
] + concatim.getpalette()[:744]
# convert RGBA images to P images with the common palette
for bid,l in infiles.items():
newl = []
for lm,tm,im in l:
w,h = im.size
if w == 0 or h == 0:
imp = None
else:
# must convert to RGB first for quantize() to work
imrgb = im.convert("RGB")
imp = imrgb.quantize(palette=concatim)
# now shift the colors by 8
pix = np.array(imp)
pix += 8
imp = Image.fromarray(pix)
# now replace full transparency in the original RGBA image with index 0
pixrgba = np.array(im)
alpha = pixrgba[:,:,3]
pix[alpha == 0] = 0
# now replace any half-transpareny with shadow body (index 4)
pix[(alpha > 0) & (alpha < 0xff)] = 4
# TODO: calculate shadow border
# now put the palette with the special colors
imp.putpalette(pal)
newl.append((lm,tm,imp))
infiles[bid] = newl
# encode all images according to the required format
for bid,l in infiles.items():
newl = []
for lm,tm,im in l:
if im:
w,h = im.size
data,size = fmtencoders[fmt](im)
else:
w,h = 0,0
data,size = '',0
newl.append((w,h,lm,tm,data,size))
infiles[bid] = newl
outf = open(outname, "w+") outf = open(outname, "w+")
# write the header # write the header