Created
March 22, 2014 08:45
-
-
Save qxcv/9703356 to your computer and use it in GitHub Desktop.
Script to convert from .bmp wireworlds to Golly wireworlds
This file contains 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
#!/usr/bin/python2 | |
from os.path import basename | |
from subprocess import PIPE, Popen, check_output | |
from sys import argv, exit, stdout, stderr | |
from re import compile | |
USAGE = "USAGE: {0} input.bmp|input.rle > output.rle|output.bmp\n".format(argv[0]) | |
RLE_HEADER = "x = {x}, y = {y}, rule = WireWorld\n" | |
XPM_HEADER = """/* XPM */ | |
static char *xpm__[] = {{ | |
/* columns rows colors chars-per-pixel */ | |
"{x} {y} 4 1 ", | |
". c black", | |
"B c #FF3F00", | |
"C c yellow", | |
"A c blue", | |
/* pixels */ | |
{data} | |
}};""" | |
# Note that empty is "." in RLE | |
# A is "head", B is "tail", C is "conductor" | |
def sample2rle(sample): | |
text_triple = sample.split()[1] | |
r, g, b = map(int, text_triple[1:-1].split(',')[:3]) | |
# emulate the "smart" colour handling that the COMP1100 Wireworld | |
# implementation uses | |
if r >= 128 and g >= 128 and b < 128: | |
return 'C' # conductor, yellow-ish | |
if r >= 128 and g < 128 and b < 128: | |
return 'B' # tail, reddish | |
if r < 128 and g < 128 and b >= 128: | |
return 'A' # head, bluish | |
return '.' # empty | |
def bmp2rle(path): | |
convert_output = check_output(['convert', path, 'txt:-']).splitlines() | |
size = map(int, convert_output[0].split()[-1].split(',')[:2]) | |
input_lines = convert_output[1:] | |
output_lines = [] | |
for j in xrange(size[1]): | |
input_line = input_lines[j * size[0]:(j + 1) * size[0]] | |
output_line = '' | |
current_colour = None | |
current_count = 0 | |
for elem in input_line: | |
colour = sample2rle(elem) | |
if colour != current_colour: | |
# We've ended an RLE run | |
if current_colour != None: | |
output_line += repr(current_count) + current_colour | |
current_colour = colour | |
current_count = 1 | |
else: | |
current_count += 1 | |
output_line += repr(current_count) + current_colour | |
output_lines.append(output_line) | |
total_file = RLE_HEADER.format(x=size[0], y=size[1]) + '$'.join(output_lines) + '!' | |
print(total_file) | |
def rle2xpm(rle): | |
"""We use this function to convert RLE to XPM before feeding it to | |
ImageMagick""" | |
# So that we ignore the stupid "extended RLE" metadata | |
lines = filter(lambda l: not l.strip().startswith('#'), rle.splitlines()) | |
# get the header so that we can rip dimensions out of it | |
header = lines[0] | |
# now I have TWO problems | |
size_re = compile('x *= *([0-9]+).+y *= *([0-9]+)') | |
x_size, y_size = map(int, list(size_re.match(header).groups())[:2]) | |
body = '\n'.join(lines[1:]) | |
elem_re = compile('([0-9]*)([\\.ABC])') | |
terminal_num_re = compile('([0-9]+)[^\\.ABC0-9]*$') | |
output_lines = [] | |
for rle_line in body.split('$'): | |
matches = elem_re.findall(rle_line) | |
line_str = '' | |
for num_text, cell in matches: | |
if not num_text: | |
num = 1 | |
else: | |
num = int(num_text) | |
line_str += num * cell | |
if len(line_str) < x_size: | |
line_str += '.' * (x_size - len(line_str)) | |
output_lines.append(line_str) | |
terminal_num_matches = terminal_num_re.findall(rle_line) | |
if len(terminal_num_matches) > 0: | |
# append some blank padding lines, as per spec | |
blank = '.' * x_size | |
number = int(terminal_num_matches[0]) | |
# I honestly have no idea what I am doing at this point | |
# Golly's output doesn't actually seem to match the spec, so I'm | |
# taking one row of the requested number of blanks | |
if number < 2: pass | |
output_lines += [blank] * (number - 1) | |
line_reprs = ['"' + line + '",' for line in output_lines] | |
# Strip the trailing comma on our last line | |
line_reprs[-1] = line_reprs[-1][:-1] | |
data_string = '\n'.join(line_reprs) | |
return XPM_HEADER.format(x=x_size, y=y_size, data=data_string) | |
def rle2bmp(path): | |
xpm = rle2xpm(open(path).read()) | |
fp = Popen(['convert', '-type', 'truecolor', 'xpm:-', 'bmp:-'], stdin=PIPE, stdout=PIPE, stderr=PIPE) | |
output, errors = fp.communicate(xpm) | |
if fp.returncode != 0: | |
raise Exception("convert failed") | |
stdout.write(output) | |
handlers = { | |
'bmp': bmp2rle, | |
'rle': rle2bmp, | |
} | |
if __name__ == '__main__': | |
if len(argv) != 2: | |
stderr.write(USAGE) | |
exit(1) | |
fn = argv[1] | |
ext_list = basename(fn).split('.') | |
ext = ext_list[-1] | |
if len(ext_list) < 2 or ext not in handlers.keys(): | |
stderr.write(USAGE) | |
exit(1) | |
handlers[ext](fn) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment