#!/usr/bin/env python # # Copyright (C) 2014 Johannes Schauer # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import os import struct import json from collections import defaultdict from PIL import Image ushrtmax = (1<<16)-1 def encode0(im): data = ''.join([chr(i) for i in list(im.getdata())]) size = len(data) return data,size # greedy RLE # 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 def encode1(im): pixels = im.load() w,h = im.size data = [] # these function return a tuple of the compressed string and the amount of # pixels compressed def rle_comp(x,y): # find all pixels after the first one with the same color color = pixels[x,y] if color == 0xff: # this color can't be run length encoded return raw_comp(x,y) else: count = 1 for x in range(x+1,w): if pixels[x,y] == color and count < 255: count += 1 else: break return (struct.pack(" rawl: r += rlec x += rlel else: r += rawc x += rawl data.append(r) # 4*height bytes for lineoffsets size = 4*h+sum([len(d) for d in data]) return data,size def encode23chunk(s,e,pixels,y): r = '' if pixels[s,y] < 8: colors = pixels[s,y] count = 1 else: colors = [pixels[s,y]] count = 0 for x in range(s+1,e): color = pixels[x,y] if count > 0: # rle was started if color == colors and count < 32: # same color again, increase count count+=1 else: # either new color or maximum length reached, so write current one r+=struct.pack(" 31: # new rle color, or maximum length reached so write current non rle r+=struct.pack(" 0: # write rle r+=struct.pack(">5)+1)<<5 rm = lm+w im = im.crop((lm,tm,rm,bm)) if im.mode != 'P': print "input images must have a palette" return False cursig =(fw,fh,im.getpalette()) if not sig: sig = cursig else: if sig != cursig: print "sigs must match - got:" print sig print cursig return False data,size = fmtencoders[fmt](im) infiles[bid].append((w,h,lm,tm,data,size)) if len(infiles) == 0: print "no input files detected" return False fw,fh,pal = cursig outname = os.path.join(outdir,p)+".def" print "writing to %s"%outname outf = open(outname, "w+") # write the header # full width and height are not used and not the same for all frames # in some defs, so just putting the last known value outf.write(struct.pack(" ushrtmax: print "exceeding max ushort value: %d"%offs return False lineoffs.append(offs) acc += len(d) outf.write(struct.pack("<%dH"%h, *lineoffs)) outf.write(struct.pack(" ushrtmax: print "exceeding max ushort value: %d"%offs return False lineoffs.append(offs) acc += len(e) outf.write(struct.pack("<"+"H"*(w/32)*h, *lineoffs)) for d in data: # line for e in d: # 32 pixel block outf.write(e) return True if __name__ == '__main__': import sys if len(sys.argv) != 3: print "usage: %s infile.json outdir"%sys.argv[0] exit(1) ret = makedef(sys.argv[1], sys.argv[2]) exit(0 if ret else 1)