format3 is actually stored in 32 pixel chunks, different from format2

This commit is contained in:
josch 2014-03-16 17:19:15 +01:00
parent 5b45233b5e
commit 2ee206c2d5
2 changed files with 108 additions and 93 deletions

View file

@ -94,14 +94,14 @@ def extract_def(infile,outdir,shred=True):
totalrowlength+=length totalrowlength+=length
elif fmt == 2: elif fmt == 2:
lineoffs = struct.unpack("<%dH"%h, f.read(2*h)) lineoffs = struct.unpack("<%dH"%h, f.read(2*h))
# unknown _,_ = struct.unpack("<BB", f.read(2)) # unknown
_,_ = struct.unpack("<BB", f.read(2))
for lineoff in lineoffs: for lineoff in lineoffs:
if f.tell() != offs+32+lineoff: if f.tell() != offs+32+lineoff:
print "unexpected offset: %d, expected %d"%(f.tell(),offs+32+lineoff) print "unexpected offset: %d, expected %d"%(f.tell(),offs+32+lineoff)
f.seek(offs+32+lineoff) f.seek(offs+32+lineoff)
totalrowlength=0 totalrowlength=0
while totalrowlength<w: while totalrowlength<w:
print f.tell()-32-offs
segment, = struct.unpack("<B", f.read(1)) segment, = struct.unpack("<B", f.read(1))
code = segment>>5 code = segment>>5
length = (segment&0x1f)+1 length = (segment&0x1f)+1
@ -111,16 +111,17 @@ def extract_def(infile,outdir,shred=True):
pixeldata += length*chr(code) pixeldata += length*chr(code)
totalrowlength+=length totalrowlength+=length
elif fmt == 3: elif fmt == 3:
# the first unsigned short in every w/16 byte block is the # each row is split into 32 byte long blocks which are individually encoded
# offset we want - the others have an unknown function # two bytes store the offset for each block per line
lineoffs = [struct.unpack("<"+"H"*(w/32), f.read(w/16))[0] for i in range(h)] lineoffs = [struct.unpack("<"+"H"*(w/32), f.read(w/16)) for i in range(h)]
for lineoff in lineoffs: for lineoff in lineoffs:
if f.tell() != offs+32+lineoff: for i in lineoff:
print "unexpected offset: %d, expected %d"%(f.tell(),offs+32+lineoff) if f.tell() != offs+32+i:
f.seek(offs+32+lineoff) print "unexpected offset: %d, expected %d"%(f.tell(),offs+32+i)
totalrowlength=0 f.seek(offs+32+i)
while totalrowlength<w: totalblocklength=0
while totalblocklength<32:
segment, = struct.unpack("<B", f.read(1)) segment, = struct.unpack("<B", f.read(1))
code = segment>>5 code = segment>>5
length = (segment&0x1f)+1 length = (segment&0x1f)+1
@ -128,7 +129,7 @@ def extract_def(infile,outdir,shred=True):
pixeldata += f.read(length) pixeldata += f.read(length)
else: # rle else: # rle
pixeldata += length*chr(code) pixeldata += length*chr(code)
totalrowlength+=length totalblocklength+=length
else: else:
print "unknown format: %d"%fmt print "unknown format: %d"%fmt
return False return False

View file

@ -7,8 +7,11 @@ from collections import defaultdict
from PIL import Image from PIL import Image
ushrtmax = (1<<16)-1 ushrtmax = (1<<16)-1
def encode0(im):
return ''.join([chr(i) for i in list(im.getdata())])
# greedy RLE # greedy RLE
# for each pixel, test which encoding results in the smaller size, then apply # for each pixel, test which encoding manages to encode most data, then apply
# that encoding and look at the next pixel after the encoded chunk # that encoding and look at the next pixel after the encoded chunk
def encode1(im): def encode1(im):
pixels = im.load() pixels = im.load()
@ -56,19 +59,15 @@ def encode1(im):
result.append(r) result.append(r)
return result return result
def encode3(im): def encode23chunk(s,e,pixels,y):
pixels = im.load()
w,h = im.size
result = []
for y in range(h):
r = '' r = ''
if pixels[0,y] < 8: if pixels[s,y] < 8:
colors = pixels[0,y] colors = pixels[s,y]
count = 1 count = 1
else: else:
colors = [pixels[0,y]] colors = [pixels[s,y]]
count = 0 count = 0
for x in range(1,w): for x in range(s+1,e):
color = pixels[x,y] color = pixels[x,y]
if count > 0: if count > 0:
# rle was started # rle was started
@ -109,9 +108,33 @@ def encode3(im):
# write non rle # write non rle
r+=struct.pack("<B", (7<<5) | (len(colors)-1)) r+=struct.pack("<B", (7<<5) | (len(colors)-1))
r+=struct.pack("<%dB"%len(colors), *colors) r+=struct.pack("<%dB"%len(colors), *colors)
result.append(r) return r
# this is like encode3 but a line is not split into 32 pixel chuncks
# the reason for this might just be that format 2 images are always 32 pixel wide
def encode2(im):
pixels = im.load()
w,h = im.size
result = []
for y in range(h):
result.append(encode23chunk(0,w,pixels,y))
return result return result
# this is like encode2 but limited to only encoding blocks of 32 pixels at a time
def encode3(im):
pixels = im.load()
w,h = im.size
result = []
for y in range(h):
res = []
# encode each row in 32 pixel blocks
for i in range(w/32):
res.append(encode23chunk(i*32, (i+1)*32, pixels, y))
result.append(res)
return result
fmtencoders = [encode0,encode1,encode2,encode3]
def makedef(indir, outdir): def makedef(indir, outdir):
infiles = defaultdict(list) infiles = defaultdict(list)
sig = None sig = None
@ -150,17 +173,7 @@ def makedef(indir, outdir):
if len(fn) > 9: if len(fn) > 9:
print "filename can't be longer than 9 bytes" print "filename can't be longer than 9 bytes"
return False return False
if fmt == 0: data = fmtencoders[fmt](im)
data = ''.join([chr(i) for i in list(im.getdata())])
elif fmt == 1:
data = encode1(im)
elif fmt == 2:
data = encode3(im)
elif fmt == 3:
data = encode3(im)
else:
print "unknown format: %d"%fmt
return False
infiles[bid].append((im,t,p,j,fn,lm,tm,fmt,data)) infiles[bid].append((im,t,p,j,fn,lm,tm,fmt,data))
if len(infiles) == 0: if len(infiles) == 0:
@ -213,7 +226,7 @@ def makedef(indir, outdir):
curoffset += 32+2*h+2+sum(len(d) for d in data) curoffset += 32+2*h+2+sum(len(d) for d in data)
elif fmt == 3: elif fmt == 3:
# width/16 bytes per line as offset header # width/16 bytes per line as offset header
curoffset += 32+(w/16)*h+sum(len(d) for d in data) curoffset += 32+(w/16)*h+sum(sum([len(e) for e in d]) for d in data)
for bid,l in infiles.items(): for bid,l in infiles.items():
for im,_,p,j,_,lm,tm,fmt,data in l: for im,_,p,j,_,lm,tm,fmt,data in l:
@ -252,26 +265,27 @@ def makedef(indir, outdir):
lineoffs.append(offs) lineoffs.append(offs)
acc += len(d) acc += len(d)
outf.write(struct.pack("<%dH"%h, *lineoffs)) outf.write(struct.pack("<%dH"%h, *lineoffs))
outf.write(struct.pack("<BB", 0, 0)) # unknown function outf.write(struct.pack("<BB", 0, 0)) # unknown meaning
for i in data: for i in data:
outf.write(i) outf.write(i)
elif fmt == 3: elif fmt == 3:
s = (w/16)*h+sum(len(d) for d in data) s = (w/16)*h+sum(sum([len(e) for e in d]) for d in data)
outf.write(struct.pack("<IIIIIIii",s,fmt,fw,fh,w,h,lm,tm)) outf.write(struct.pack("<IIIIIIii",s,fmt,fw,fh,w,h,lm,tm))
# store the same value in all w/16 blocks per line # store the offsets for all 32 pixel blocks
lineoffs = []
acc = 0 acc = 0
lineoffs = []
for d in data: for d in data:
for e in d:
offs = acc+(w/16)*h offs = acc+(w/16)*h
if offs > ushrtmax: if offs > ushrtmax:
print "exceeding max ushort value: %d"%offs print "exceeding max ushort value: %d"%offs
return False return False
lineoffs.append(offs) lineoffs.append(offs)
lineoffs.extend([0 for i in range(w/32-1)]) acc += len(e)
acc += len(d)
outf.write(struct.pack("<"+"H"*(w/32)*h, *lineoffs)) outf.write(struct.pack("<"+"H"*(w/32)*h, *lineoffs))
for i in data: for d in data: # line
outf.write(i) for e in d: # 32 pixel block
outf.write(e)
return True return True
if __name__ == '__main__': if __name__ == '__main__':